Skip to content

Commit

Permalink
microsoft#67076 Add quick fixes action
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Feb 22, 2019
1 parent 3b8f75a commit 4c81fb4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/vs/editor/contrib/hover/hover.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
}

.monaco-editor-hover .hover-row .actions .action-container {
padding-left: 8px;
padding: 0px 8px;
}

.monaco-editor-hover .hover-row .actions .action-container .action {
Expand Down
8 changes: 7 additions & 1 deletion src/vs/editor/contrib/hover/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCod
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICommandService } from 'vs/platform/commands/common/commands';

export class ModesHoverController implements IEditorContribution {

Expand Down Expand Up @@ -66,6 +69,9 @@ export class ModesHoverController implements IEditorContribution {
@IModeService private readonly _modeService: IModeService,
@IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@ICommandService private readonly _commandService: ICommandService,
@IThemeService private readonly _themeService: IThemeService
) {
this._toUnhook = [];
Expand Down Expand Up @@ -209,7 +215,7 @@ export class ModesHoverController implements IEditorContribution {

private _createHoverWidget() {
const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService);
this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._markerDecorationsService, this._themeService, this._keybindingService, this._openerService);
this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._markerDecorationsService, this._themeService, this._keybindingService, this._contextMenuService, this._bulkEditService, this._commandService, this._openerService);
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, renderer);
}

Expand Down
62 changes: 54 additions & 8 deletions src/vs/editor/contrib/hover/modesContentHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
Expand All @@ -31,6 +31,14 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction, QuickFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { Action } from 'vs/base/common/actions';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';

const $ = dom.$;

Expand Down Expand Up @@ -209,6 +217,9 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
markerDecorationsService: IMarkerDecorationsService,
private readonly _themeService: IThemeService,
private readonly _keybindingService: IKeybindingService,
private readonly _contextMenuService: IContextMenuService,
private readonly _bulkEditService: IBulkEditService,
private readonly _commandService: ICommandService,
private readonly _openerService: IOpenerService | null = NullOpenerService,
) {
super(ModesContentHoverWidget.ID, editor);
Expand Down Expand Up @@ -513,36 +524,71 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
const disposables: IDisposable[] = [];
const actionsElement = dom.append(hoverElement, $('div.actions'));

const keybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID);
const label = nls.localize('peek problem', "Peek Problem");
disposables.push(this.renderAction(actionsElement, {
label, iconClass: 'octicon.octicon-eye', keybinding: keybinding ? keybinding.getLabel() : '', run: () => {
label: nls.localize('peek problem', "Peek Problem"),
iconClass: 'octicon.octicon-eye',
commandId: NextMarkerAction.ID,
run: () => {
this.hide();
MarkerController.get(this._editor).show(markerHover.marker);
this._editor.focus();
}
}));

disposables.push(this.renderAction(actionsElement, {
label: nls.localize('quick fixes', "Quick Fix..."),
iconClass: 'octicon.octicon-light-bulb',
commandId: QuickFixAction.Id,
run: async (target) => {
const codeActionsPromise = this.getCodeActions(markerHover.marker);
disposables.push(toDisposable(() => codeActionsPromise.cancel()));
const actions = await codeActionsPromise;
const elementPosition = dom.getDomNodePagePosition(target);
this._contextMenuService.showContextMenu({
getAnchor: () => ({ x: elementPosition.left + 6, y: elementPosition.top + elementPosition.height + 6 }),
getActions: () => actions
});
}
}));

this.renderDisposable = combinedDisposable(disposables);
return hoverElement;
}

private renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: () => void, keybinding?: string }): IDisposable {
private getCodeActions(marker: IMarker): CancelablePromise<Action[]> {
return createCancelablePromise(async cancellationToken => {
const codeActions = await getCodeActions(this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, cancellationToken);
if (codeActions.length) {
return codeActions.map(codeAction => new Action(
codeAction.command ? codeAction.command.id : codeAction.title,
codeAction.title,
undefined,
true,
() => applyCodeAction(codeAction, this._bulkEditService, this._commandService)));
}
return [
new Action('', nls.localize('editor.action.quickFix.noneMessage', "No code actions available"))
];
});
}

private renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }): IDisposable {
const actionContainer = dom.append(parent, $('div.action-container'));
const action = dom.append(actionContainer, $('a.action'));
if (actionOptions.iconClass) {
dom.append(action, $(`span.icon.${actionOptions.iconClass}`));
}
const label = dom.append(action, $('span'));
label.textContent = actionOptions.label;
if (actionOptions.keybinding) {
const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId);
if (keybinding) {
const actionKeybindingLabel = dom.append(actionContainer, $('span.keybinding-label'));
actionKeybindingLabel.textContent = actionOptions.keybinding;
actionKeybindingLabel.textContent = keybinding.getLabel();
}
return dom.addDisposableListener(action, dom.EventType.CLICK, e => {
e.stopPropagation();
e.preventDefault();
actionOptions.run();
actionOptions.run(action);
});
}

Expand Down

0 comments on commit 4c81fb4

Please sign in to comment.