Skip to content

Commit d5b9f66

Browse files
committed
goto error - extract quick fix widget
1 parent 7d8f715 commit d5b9f66

1 file changed

Lines changed: 119 additions & 101 deletions

File tree

src/vs/editor/contrib/gotoError/browser/gotoError.ts

Lines changed: 119 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/edito
2929
import {ICodeEditor} from 'vs/editor/browser/editorBrowser';
3030
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
3131
import {IOptions, ZoneWidget} from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
32-
import {getCodeActions} from 'vs/editor/contrib/quickFix/common/quickFix';
32+
import {getCodeActions, IQuickFix2} from 'vs/editor/contrib/quickFix/common/quickFix';
3333

3434
class MarkerModel {
3535

@@ -181,6 +181,117 @@ class MarkerModel {
181181
}
182182
}
183183

184+
class FixesWidget {
185+
186+
domNode: HTMLDivElement;
187+
188+
private _disposeOnUpdate: IDisposable[] = [];
189+
private _listener: IDisposable;
190+
191+
constructor(
192+
container: HTMLElement,
193+
@IKeybindingService private _keybindingService: IKeybindingService
194+
) {
195+
this.domNode = document.createElement('div');
196+
container.appendChild(this.domNode);
197+
198+
this._listener = dom.addStandardDisposableListener(container, 'keydown', (e) => {
199+
switch (e.asKeybinding()) {
200+
case CommonKeybindings.LEFT_ARROW:
201+
this._move(true);
202+
// this._goLeft();
203+
e.preventDefault();
204+
e.stopPropagation();
205+
break;
206+
case CommonKeybindings.RIGHT_ARROW:
207+
this._move(false);
208+
// this._goRight();
209+
e.preventDefault();
210+
e.stopPropagation();
211+
break;
212+
}
213+
});
214+
}
215+
216+
dispose(): void {
217+
this._disposeOnUpdate = dispose(this._disposeOnUpdate);
218+
this._listener = dispose(this._listener);
219+
}
220+
221+
update(fixes: TPromise<IQuickFix2[]>): TPromise<any> {
222+
this._disposeOnUpdate = dispose(this._disposeOnUpdate);
223+
this.domNode.style.display = 'none';
224+
return fixes.then(fixes => this._doUpdate(fixes), onUnexpectedError);
225+
}
226+
227+
private _doUpdate(fixes: IQuickFix2[]): void {
228+
229+
dom.clearNode(this.domNode);
230+
231+
if (!fixes || fixes.length === 0) {
232+
return;
233+
}
234+
235+
// light bulb and label
236+
let quickfixhead = document.createElement('span');
237+
quickfixhead.className = 'quickfixhead';
238+
quickfixhead.appendChild(document.createTextNode(fixes.length > 1
239+
? nls.localize('quickfix.multiple.label', 'Suggested fixes: ')
240+
: nls.localize('quickfix.single.label', 'Suggested fix: ')));
241+
this.domNode.appendChild(quickfixhead);
242+
243+
// each fix as entry
244+
const container = document.createElement('span');
245+
container.className = 'quickfixcontainer';
246+
247+
fixes.forEach((fix, idx, arr) => {
248+
249+
if (idx > 0) {
250+
let separator = document.createElement('span');
251+
separator.appendChild(document.createTextNode(', '));
252+
container.appendChild(separator);
253+
}
254+
255+
let entry = document.createElement('a');
256+
entry.tabIndex = 0;
257+
entry.className = `quickfixentry`;
258+
entry.dataset['idx'] = String(idx);
259+
entry.dataset['next'] = String(idx < arr.length - 1 ? idx + 1 : 0);
260+
entry.dataset['prev'] = String(idx > 0 ? idx - 1 : arr.length - 1);
261+
entry.appendChild(document.createTextNode(fix.command.title));
262+
this._disposeOnUpdate.push(dom.addDisposableListener(entry, dom.EventType.CLICK, () => {
263+
this._keybindingService.executeCommand(fix.command.id, ...fix.command.arguments);
264+
return true;
265+
}));
266+
this._disposeOnUpdate.push(dom.addStandardDisposableListener(entry, 'keydown', (e) => {
267+
switch (e.asKeybinding()) {
268+
case CommonKeybindings.ENTER:
269+
case CommonKeybindings.SPACE:
270+
this._keybindingService.executeCommand(fix.command.id, ...fix.command.arguments);
271+
e.preventDefault();
272+
e.stopPropagation();
273+
}
274+
}));
275+
container.appendChild(entry);
276+
});
277+
278+
this.domNode.appendChild(container);
279+
this.domNode.style.display = '';
280+
}
281+
282+
private _move(left: boolean): void {
283+
let target: HTMLElement;
284+
if (document.activeElement.classList.contains('quickfixentry')) {
285+
let current = <HTMLElement> document.activeElement;
286+
let idx = left ? current.dataset['prev'] : current.dataset['next'];
287+
target = <HTMLElement>this.domNode.querySelector(`a[data-idx='${idx}']`);
288+
} else {
289+
target = <HTMLElement> this.domNode.querySelector('.quickfixentry');
290+
}
291+
target.focus();
292+
}
293+
}
294+
184295
var zoneOptions: IOptions = {
185296
showFrame: true,
186297
showArrow: true,
@@ -191,10 +302,8 @@ class MarkerNavigationWidget extends ZoneWidget {
191302

192303
private _container: HTMLElement;
193304
private _element: HTMLElement;
194-
private _quickFixSection: HTMLElement;
305+
private _fixesWidget: FixesWidget;
195306
private _callOnDispose: IDisposable[] = [];
196-
private _localCleanup: IDisposable[] = [];
197-
private _quickFixEntries: HTMLElement[];
198307

199308
constructor(editor: ICodeEditor, private _model: MarkerModel, private _keybindingService: IKeybindingService) {
200309
super(editor, zoneOptions);
@@ -215,45 +324,8 @@ class MarkerNavigationWidget extends ZoneWidget {
215324
this._element.setAttribute('role', 'alert');
216325
this._container.appendChild(this._element);
217326

218-
this._quickFixSection = document.createElement('div');
219-
this._container.appendChild(this._quickFixSection);
220-
221-
this._callOnDispose.push(dom.addStandardDisposableListener(this._container, 'keydown', (e) => {
222-
switch (e.asKeybinding()) {
223-
case CommonKeybindings.LEFT_ARROW:
224-
this._goLeft();
225-
e.preventDefault();
226-
e.stopPropagation();
227-
break;
228-
case CommonKeybindings.RIGHT_ARROW:
229-
this._goRight();
230-
e.preventDefault();
231-
e.stopPropagation();
232-
break;
233-
234-
}
235-
}));
236-
}
237-
238-
private _goLeft(): void {
239-
if (!this._quickFixEntries) {
240-
return;
241-
}
242-
let idx = this._quickFixEntries.indexOf(<HTMLElement>document.activeElement);
243-
if (idx === -1) {
244-
idx = 1;
245-
}
246-
idx = (idx + this._quickFixEntries.length - 1) % this._quickFixEntries.length;
247-
this._quickFixEntries[idx].focus();
248-
}
249-
250-
private _goRight(): void {
251-
if (!this._quickFixEntries) {
252-
return;
253-
}
254-
let idx = this._quickFixEntries.indexOf(<HTMLElement>document.activeElement);
255-
idx = (idx + 1) % this._quickFixEntries.length;
256-
this._quickFixEntries[idx].focus();
327+
this._fixesWidget = new FixesWidget(this._container, this._keybindingService);
328+
this._callOnDispose.push(this._fixesWidget);
257329
}
258330

259331
public show(where: editorCommon.IPosition, heightInLines: number): void {
@@ -282,8 +354,6 @@ class MarkerNavigationWidget extends ZoneWidget {
282354
break;
283355
}
284356

285-
this._localCleanup = dispose(this._localCleanup);
286-
287357
// update label and show
288358
let text = strings.format('({0}/{1}) ', this._model.indexOf(marker) + 1, this._model.length());
289359
if (marker.source) {
@@ -292,62 +362,10 @@ class MarkerNavigationWidget extends ZoneWidget {
292362
dom.clearNode(this._element);
293363
this._element.appendChild(document.createTextNode(text));
294364
this._element.appendChild(renderHtml(marker.message));
295-
this._quickFixSection.style.display = 'none';
296-
297-
getCodeActions(this.editor.getModel(), Range.lift(marker)).then(result => {
298-
dom.clearNode(this._quickFixSection);
299-
300-
if (result.length > 0) {
301-
302-
this._localCleanup.push({
303-
dispose: () => {
304-
this._quickFixEntries = [];
305-
}
306-
});
307-
308-
let quickfixhead = document.createElement('span');
309-
quickfixhead.className = 'quickfixhead';
310-
quickfixhead.appendChild(document.createTextNode(result.length > 1 ? nls.localize('quickfix.multiple.label', 'Suggested fixes: ') : nls.localize('quickfix.single.label', 'Suggested fix: ')));
311-
this._quickFixSection.appendChild(quickfixhead);
312-
313-
this._quickFixEntries = [];
314-
let quickfixcontainer = document.createElement('span');
315-
quickfixcontainer.className = 'quickfixcontainer';
316-
result.forEach((fix, idx, arr) => {
317-
var container = quickfixcontainer;
318-
if (idx > 0) {
319-
let separator = document.createElement('span');
320-
separator.appendChild(document.createTextNode(', '));
321-
container.appendChild(separator);
322-
}
323-
324-
let entry = document.createElement('a');
325-
entry.tabIndex = 0;
326-
entry.className = 'quickfixentry';
327-
entry.appendChild(document.createTextNode(fix.command.title));
328-
this._localCleanup.push(dom.addDisposableListener(entry, dom.EventType.CLICK, () => {
329-
this._keybindingService.executeCommand(fix.command.id, ...fix.command.arguments);
330-
return true;
331-
}));
332-
this._localCleanup.push(dom.addStandardDisposableListener(entry, 'keydown', (e) => {
333-
switch (e.asKeybinding()) {
334-
case CommonKeybindings.ENTER:
335-
case CommonKeybindings.SPACE:
336-
this._keybindingService.executeCommand(fix.command.id, ...fix.command.arguments);
337-
e.preventDefault();
338-
e.stopPropagation();
339-
}
340-
}));
341-
container.appendChild(entry);
342-
343-
this._quickFixEntries.push(entry);
344-
});
345-
this._quickFixSection.appendChild(quickfixcontainer);
346-
347-
this._quickFixSection.style.display = '';
348-
this.show(new Position(marker.startLineNumber, marker.startColumn), 4);
349-
}
350-
}, onUnexpectedError);
365+
366+
this._fixesWidget
367+
.update(getCodeActions(this.editor.getModel(), Range.lift(marker)))
368+
.then(() => this.show(new Position(marker.startLineNumber, marker.startColumn), 4));
351369

352370
this._model.withoutWatchingEditorPosition(() => this.show(new Position(marker.startLineNumber, marker.startColumn), 3));
353371
}

0 commit comments

Comments
 (0)