Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
@@ -92,7 +92,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
|
||||
private _lastBracketsData: BracketsData[];
|
||||
private _lastVersionId: number;
|
||||
private _decorations: string[];
|
||||
private _updateBracketsSoon: RunOnceScheduler;
|
||||
private readonly _updateBracketsSoon: RunOnceScheduler;
|
||||
private _matchBrackets: boolean;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -12,7 +12,7 @@ import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCom
|
||||
|
||||
class MoveCaretAction extends EditorAction {
|
||||
|
||||
private left: boolean;
|
||||
private readonly left: boolean;
|
||||
|
||||
constructor(left: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
|
||||
@@ -10,8 +10,8 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export class MoveCaretCommand implements ICommand {
|
||||
|
||||
private _selection: Selection;
|
||||
private _isMovingLeft: boolean;
|
||||
private readonly _selection: Selection;
|
||||
private readonly _isMovingLeft: boolean;
|
||||
|
||||
private _cutStartIndex: number;
|
||||
private _cutEndIndex: number;
|
||||
|
||||
@@ -32,7 +32,7 @@ type ExecCommand = 'cut' | 'copy' | 'paste';
|
||||
|
||||
abstract class ExecCommandAction extends EditorAction {
|
||||
|
||||
private browserCommand: ExecCommand;
|
||||
private readonly browserCommand: ExecCommand;
|
||||
|
||||
constructor(browserCommand: ExecCommand, opts: IActionOptions) {
|
||||
super(opts);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
@@ -13,14 +13,41 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind, CodeActionFilter } from './codeActionTrigger';
|
||||
import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger';
|
||||
|
||||
export class CodeActionSet {
|
||||
|
||||
private static codeActionsComparator(a: CodeAction, b: CodeAction): number {
|
||||
if (isNonEmptyArray(a.diagnostics)) {
|
||||
if (isNonEmptyArray(b.diagnostics)) {
|
||||
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (isNonEmptyArray(b.diagnostics)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0; // both have no diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
public readonly actions: ReadonlyArray<CodeAction>;
|
||||
|
||||
public constructor(actions: CodeAction[]) {
|
||||
this.actions = mergeSort(actions, CodeActionSet.codeActionsComparator);
|
||||
}
|
||||
|
||||
public get hasAutoFix() {
|
||||
return this.actions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred);
|
||||
}
|
||||
}
|
||||
|
||||
export function getCodeActions(
|
||||
model: ITextModel,
|
||||
rangeOrSelection: Range | Selection,
|
||||
trigger: CodeActionTrigger,
|
||||
token: CancellationToken
|
||||
): Promise<CodeAction[]> {
|
||||
): Promise<CodeActionSet> {
|
||||
const filter = trigger.filter || {};
|
||||
|
||||
const codeActionContext: CodeActionContext = {
|
||||
@@ -28,12 +55,13 @@ export function getCodeActions(
|
||||
trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
|
||||
};
|
||||
|
||||
if (filter.kind && CodeActionKind.Source.contains(filter.kind) && rangeOrSelection.isEmpty()) {
|
||||
rangeOrSelection = model.getFullModelRange();
|
||||
}
|
||||
const chainedCancellation = new CancellationTokenSource();
|
||||
token.onCancellationRequested(() => chainedCancellation.cancel());
|
||||
|
||||
const promises = getCodeActionProviders(model, filter).map(provider => {
|
||||
return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => {
|
||||
const providers = getCodeActionProviders(model, filter);
|
||||
|
||||
const promises = providers.map(provider => {
|
||||
return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, chainedCancellation.token)).then(providedCodeActions => {
|
||||
if (!Array.isArray(providedCodeActions)) {
|
||||
return [];
|
||||
}
|
||||
@@ -48,9 +76,19 @@ export function getCodeActions(
|
||||
});
|
||||
});
|
||||
|
||||
const listener = CodeActionProviderRegistry.onDidChange(() => {
|
||||
const newProviders = CodeActionProviderRegistry.all(model);
|
||||
if (!equals(newProviders, providers)) {
|
||||
chainedCancellation.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(flatten)
|
||||
.then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator));
|
||||
.then(actions => new CodeActionSet(actions))
|
||||
.finally(() => {
|
||||
listener.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
function getCodeActionProviders(
|
||||
@@ -68,22 +106,8 @@ function getCodeActionProviders(
|
||||
});
|
||||
}
|
||||
|
||||
function codeActionsComparator(a: CodeAction, b: CodeAction): number {
|
||||
if (isNonEmptyArray(a.diagnostics)) {
|
||||
if (isNonEmptyArray(b.diagnostics)) {
|
||||
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (isNonEmptyArray(b.diagnostics)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0; // both have no diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
registerLanguageCommand('_executeCodeActionProvider', function (accessor, args) {
|
||||
const { resource, range } = args;
|
||||
registerLanguageCommand('_executeCodeActionProvider', function (accessor, args): Promise<ReadonlyArray<CodeAction>> {
|
||||
const { resource, range, kind } = args;
|
||||
if (!(resource instanceof URI) || !Range.isIRange(range)) {
|
||||
throw illegalArgument();
|
||||
}
|
||||
@@ -96,6 +120,6 @@ registerLanguageCommand('_executeCodeActionProvider', function (accessor, args)
|
||||
return getCodeActions(
|
||||
model,
|
||||
model.validateRange(range),
|
||||
{ type: 'manual', filter: { includeSourceActions: true } },
|
||||
CancellationToken.None);
|
||||
{ type: 'manual', filter: { includeSourceActions: true, kind: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
|
||||
CancellationToken.None).then(actions => actions.actions);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { CancelablePromise } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
@@ -27,6 +27,7 @@ import { CodeActionContextMenu } from './codeActionWidget';
|
||||
import { LightBulbWidget } from './lightBulbWidget';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
|
||||
function contextKeyForSupportedActions(kind: CodeActionKind) {
|
||||
return ContextKeyExpr.regex(
|
||||
@@ -34,7 +35,7 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
|
||||
new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b'));
|
||||
}
|
||||
|
||||
export class QuickFixController implements IEditorContribution {
|
||||
export class QuickFixController extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.quickFixController';
|
||||
|
||||
@@ -42,15 +43,15 @@ export class QuickFixController implements IEditorContribution {
|
||||
return editor.getContribution<QuickFixController>(QuickFixController.ID);
|
||||
}
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _model: CodeActionModel;
|
||||
private _codeActionContextMenu: CodeActionContextMenu;
|
||||
private _lightBulbWidget: LightBulbWidget;
|
||||
private _disposables: IDisposable[] = [];
|
||||
private readonly _editor: ICodeEditor;
|
||||
private readonly _model: CodeActionModel;
|
||||
private readonly _codeActionContextMenu: CodeActionContextMenu;
|
||||
private readonly _lightBulbWidget: LightBulbWidget;
|
||||
|
||||
private _activeRequest: CancelablePromise<CodeAction[]> | undefined;
|
||||
private _activeRequest: CancelablePromise<CodeActionSet> | undefined;
|
||||
|
||||
constructor(editor: ICodeEditor,
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IMarkerService markerService: IMarkerService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IProgressService progressService: IProgressService,
|
||||
@@ -59,24 +60,24 @@ export class QuickFixController implements IEditorContribution {
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._editor = editor;
|
||||
this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService);
|
||||
this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action));
|
||||
this._lightBulbWidget = new LightBulbWidget(editor);
|
||||
this._lightBulbWidget = this._register(new LightBulbWidget(editor));
|
||||
|
||||
this._updateLightBulbTitle();
|
||||
|
||||
this._disposables.push(
|
||||
this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} })),
|
||||
this._lightBulbWidget.onClick(this._handleLightBulbSelect, this),
|
||||
this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e)),
|
||||
this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)
|
||||
);
|
||||
this._register(this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} })));
|
||||
this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this));
|
||||
this._register(this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e)));
|
||||
this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this._model.dispose();
|
||||
dispose(this._disposables);
|
||||
}
|
||||
|
||||
private _onDidChangeCodeActionsState(newState: CodeActionsState.State): void {
|
||||
@@ -91,10 +92,10 @@ export class QuickFixController implements IEditorContribution {
|
||||
if (newState.trigger.filter && newState.trigger.filter.kind) {
|
||||
// Triggered for specific scope
|
||||
newState.actions.then(fixes => {
|
||||
if (fixes.length > 0) {
|
||||
if (fixes.actions.length > 0) {
|
||||
// Apply if we only have one action or requested autoApply
|
||||
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
|
||||
this._onApplyCodeAction(fixes[0]);
|
||||
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.actions.length === 1)) {
|
||||
this._onApplyCodeAction(fixes.actions[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +127,7 @@ export class QuickFixController implements IEditorContribution {
|
||||
this._codeActionContextMenu.show(e.state.actions, e);
|
||||
}
|
||||
|
||||
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise<CodeAction[] | undefined> {
|
||||
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise<CodeActionSet | undefined> {
|
||||
return this._model.trigger({ type: 'manual', filter, autoApply });
|
||||
}
|
||||
|
||||
@@ -177,7 +178,7 @@ function showCodeActionsForEditorSelection(
|
||||
|
||||
const pos = editor.getPosition();
|
||||
controller.triggerFromEditorSelection(filter, autoApply).then(codeActions => {
|
||||
if (!codeActions || !codeActions.length) {
|
||||
if (!codeActions || !codeActions.actions.length) {
|
||||
MessageController.get(editor).showMessage(notAvailableMessage, pos);
|
||||
}
|
||||
});
|
||||
@@ -253,7 +254,27 @@ export class CodeActionCommand extends EditorCommand {
|
||||
constructor() {
|
||||
super({
|
||||
id: CodeActionCommand.Id,
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider)
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
|
||||
description: {
|
||||
description: `Trigger a code action`,
|
||||
args: [{
|
||||
name: 'args',
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'required': ['kind'],
|
||||
'properties': {
|
||||
'kind': {
|
||||
'type': 'string'
|
||||
},
|
||||
'apply': {
|
||||
'type': 'string',
|
||||
'default': 'ifSingle',
|
||||
'enum': ['first', 'ifSingle', 'never']
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -297,6 +318,25 @@ export class RefactorAction extends EditorAction {
|
||||
when: ContextKeyExpr.and(
|
||||
EditorContextKeys.writable,
|
||||
contextKeyForSupportedActions(CodeActionKind.Refactor)),
|
||||
},
|
||||
description: {
|
||||
description: 'Refactor...',
|
||||
args: [{
|
||||
name: 'args',
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'kind': {
|
||||
'type': 'string'
|
||||
},
|
||||
'apply': {
|
||||
'type': 'string',
|
||||
'default': 'never',
|
||||
'enum': ['first', 'ifSingle', 'never']
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -333,6 +373,25 @@ export class SourceAction extends EditorAction {
|
||||
when: ContextKeyExpr.and(
|
||||
EditorContextKeys.writable,
|
||||
contextKeyForSupportedActions(CodeActionKind.Source)),
|
||||
},
|
||||
description: {
|
||||
description: 'Source Action...',
|
||||
args: [{
|
||||
name: 'args',
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'kind': {
|
||||
'type': 'string'
|
||||
},
|
||||
'apply': {
|
||||
'type': 'string',
|
||||
'default': 'never',
|
||||
'enum': ['first', 'ifSingle', 'never']
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -381,6 +440,29 @@ export class OrganizeImportsAction extends EditorAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class FixAllAction extends EditorAction {
|
||||
|
||||
static readonly Id = 'editor.action.fixAll';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: FixAllAction.Id,
|
||||
label: nls.localize('fixAll.label', "Fix All"),
|
||||
alias: 'Fix All',
|
||||
precondition: ContextKeyExpr.and(
|
||||
EditorContextKeys.writable,
|
||||
contextKeyForSupportedActions(CodeActionKind.SourceFixAll))
|
||||
});
|
||||
}
|
||||
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
return showCodeActionsForEditorSelection(editor,
|
||||
nls.localize('fixAll.noneMessage', "No fix all action available"),
|
||||
{ kind: CodeActionKind.SourceFixAll, includeSourceActions: true },
|
||||
CodeActionAutoApply.IfSingle);
|
||||
}
|
||||
}
|
||||
|
||||
export class AutoFixAction extends EditorAction {
|
||||
|
||||
static readonly Id = 'editor.action.autoFix';
|
||||
|
||||
@@ -11,11 +11,11 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { CodeActionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { getCodeActions } from './codeAction';
|
||||
import { getCodeActions, CodeActionSet } from './codeAction';
|
||||
import { CodeActionTrigger } from './codeActionTrigger';
|
||||
|
||||
export const SUPPORTED_CODE_ACTIONS = new RawContextKey<string>('supportedCodeAction', '');
|
||||
@@ -26,9 +26,9 @@ export class CodeActionOracle {
|
||||
private readonly _autoTriggerTimer = new TimeoutTimer();
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _markerService: IMarkerService,
|
||||
private _signalChange: (newState: CodeActionsState.State) => void,
|
||||
private readonly _signalChange: (newState: CodeActionsState.State) => void,
|
||||
private readonly _delay: number = 250,
|
||||
private readonly _progressService?: IProgressService,
|
||||
) {
|
||||
@@ -112,7 +112,7 @@ export class CodeActionOracle {
|
||||
return selection ? selection : undefined;
|
||||
}
|
||||
|
||||
private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Promise<CodeAction[] | undefined> {
|
||||
private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Promise<CodeActionSet | undefined> {
|
||||
if (!selection) {
|
||||
// cancel
|
||||
this._signalChange(CodeActionsState.Empty);
|
||||
@@ -160,7 +160,7 @@ export namespace CodeActionsState {
|
||||
public readonly trigger: CodeActionTrigger,
|
||||
public readonly rangeOrSelection: Range | Selection,
|
||||
public readonly position: Position,
|
||||
public readonly actions: CancelablePromise<CodeAction[]>,
|
||||
public readonly actions: CancelablePromise<CodeActionSet>,
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -169,18 +169,18 @@ export namespace CodeActionsState {
|
||||
|
||||
export class CodeActionModel {
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _markerService: IMarkerService;
|
||||
private _codeActionOracle?: CodeActionOracle;
|
||||
private _state: CodeActionsState.State = CodeActionsState.Empty;
|
||||
private _onDidChangeState = new Emitter<CodeActionsState.State>();
|
||||
private _disposables: IDisposable[] = [];
|
||||
private readonly _supportedCodeActions: IContextKey<string>;
|
||||
|
||||
constructor(editor: ICodeEditor, markerService: IMarkerService, contextKeyService: IContextKeyService, private readonly _progressService: IProgressService) {
|
||||
this._editor = editor;
|
||||
this._markerService = markerService;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _markerService: IMarkerService,
|
||||
contextKeyService: IContextKeyService,
|
||||
private readonly _progressService: IProgressService
|
||||
) {
|
||||
this._supportedCodeActions = SUPPORTED_CODE_ACTIONS.bindTo(contextKeyService);
|
||||
|
||||
this._disposables.push(this._editor.onDidChangeModel(() => this._update()));
|
||||
@@ -206,7 +206,7 @@ export class CodeActionModel {
|
||||
}
|
||||
|
||||
if (this._state.type === CodeActionsState.Type.Triggered) {
|
||||
// this._state.actions.cancel();
|
||||
this._state.actions.cancel();
|
||||
}
|
||||
this.setState(CodeActionsState.Empty);
|
||||
|
||||
@@ -231,7 +231,7 @@ export class CodeActionModel {
|
||||
}
|
||||
}
|
||||
|
||||
public trigger(trigger: CodeActionTrigger): Promise<CodeAction[] | undefined> {
|
||||
public trigger(trigger: CodeActionTrigger): Promise<CodeActionSet | undefined> {
|
||||
if (this._codeActionOracle) {
|
||||
return this._codeActionOracle.trigger(trigger);
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ import { Position } from 'vs/editor/common/core/position';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { CodeAction } from 'vs/editor/common/modes';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
|
||||
export class CodeActionContextMenu {
|
||||
|
||||
private _visible: boolean;
|
||||
private _onDidExecuteCodeAction = new Emitter<void>();
|
||||
|
||||
readonly onDidExecuteCodeAction: Event<void> = this._onDidExecuteCodeAction.event;
|
||||
private readonly _onDidExecuteCodeAction = new Emitter<void>();
|
||||
public readonly onDidExecuteCodeAction: Event<void> = this._onDidExecuteCodeAction.event;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@@ -26,13 +27,14 @@ export class CodeActionContextMenu {
|
||||
private readonly _onApplyCodeAction: (action: CodeAction) => Promise<any>
|
||||
) { }
|
||||
|
||||
async show(actionsToShow: Promise<CodeAction[]>, at?: { x: number; y: number } | Position): Promise<void> {
|
||||
async show(actionsToShow: Promise<CodeActionSet>, at?: { x: number; y: number } | Position): Promise<void> {
|
||||
const codeActions = await actionsToShow;
|
||||
if (!this._editor.getDomNode()) {
|
||||
// cancel when editor went off-dom
|
||||
return Promise.reject(canceled());
|
||||
}
|
||||
const actions = codeActions.map(action => this.codeActionToAction(action));
|
||||
this._visible = true;
|
||||
const actions = codeActions.actions.map(action => this.codeActionToAction(action));
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => {
|
||||
if (Position.isIPosition(at)) {
|
||||
@@ -51,7 +53,7 @@ export class CodeActionContextMenu {
|
||||
|
||||
private codeActionToAction(action: CodeAction): Action {
|
||||
const id = action.command ? action.command.id : action.title;
|
||||
const title = action.isPreferred ? `${action.title} ★` : action.title;
|
||||
const title = action.title;
|
||||
return new Action(id, title, undefined, true, () =>
|
||||
this._onApplyCodeAction(action)
|
||||
.finally(() => this._onDidExecuteCodeAction.fire(undefined)));
|
||||
@@ -69,7 +71,7 @@ export class CodeActionContextMenu {
|
||||
this._editor.render();
|
||||
|
||||
// Translate to absolute editor position
|
||||
const cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition());
|
||||
const cursorCoords = this._editor.getScrolledVisiblePosition(position);
|
||||
const editorCoords = getDomNodePagePosition(this._editor.getDomNode());
|
||||
const x = editorCoords.left + cursorCoords.left;
|
||||
const y = editorCoords.top + cursorCoords.top + cursorCoords.height;
|
||||
|
||||
@@ -21,7 +21,16 @@
|
||||
background: url('lightbulb.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs .lightbulb-glyph.autofixable {
|
||||
background: url('lightbulb-autofix.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .lightbulb-glyph,
|
||||
.monaco-editor.hc-black .lightbulb-glyph {
|
||||
background: url('lightbulb-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .lightbulb-glyph.autofixable,
|
||||
.monaco-editor.hc-black .lightbulb-glyph.autofixable {
|
||||
background: url('lightbulb-autofix-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import 'vs/css!./lightBulbWidget';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { CodeActionsState } from './codeActionModel';
|
||||
|
||||
export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
@@ -126,8 +127,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
|
||||
const selection = this._state.rangeOrSelection;
|
||||
this._state.actions.then(fixes => {
|
||||
if (!token.isCancellationRequested && fixes && fixes.length > 0 && selection) {
|
||||
this._show();
|
||||
if (!token.isCancellationRequested && fixes.actions.length > 0 && selection) {
|
||||
this._show(fixes);
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
@@ -144,7 +145,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
return this._domNode.title;
|
||||
}
|
||||
|
||||
private _show(): void {
|
||||
private _show(codeActions: CodeActionSet): void {
|
||||
const config = this._editor.getConfiguration();
|
||||
if (!config.contribInfo.lightbulbEnabled) {
|
||||
return;
|
||||
@@ -184,6 +185,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
position: { lineNumber: effectiveLineNumber, column: 1 },
|
||||
preference: LightBulbWidget._posPref
|
||||
};
|
||||
dom.toggleClass(this._domNode, 'autofixable', codeActions.hasAutoFix);
|
||||
this._editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
|
||||
10
src/vs/editor/contrib/codeAction/lightbulb-autofix-dark.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.001 4.2C12.601 2.1 10.301 0 8.801 0H6.201C5.801 0 5.601 0.2 5.601 0.2C3.501 0.8 2.001 2.7 2.001 4.9C2.001 5.4 1.901 7.2 3.701 8.7C4.201 9.2 4.901 10.7 5.001 11.1V14.4L6.601 16H8.601L10.101 14.4V11C10.201 10.6 10.901 9.1 11.401 8.7C12.501 7.8 12.901 6.8 13.001 6V4.2Z" fill="#1E1E1E"/>
|
||||
<path d="M6.00098 12H9.00098V13H6.00098V12ZM7.00098 15H8.10098L9.00098 14H6.00098L7.00098 15Z" fill="#C5C5C5"/>
|
||||
<path d="M12.1011 4.9999C12.1011 2.6999 10.3011 0.899902 8.00107 0.899902C7.90107 0.899902 6.60107 0.999902 6.60107 0.999902C4.50107 1.2999 2.90107 2.9999 2.90107 4.9999C2.90107 5.0999 2.70107 6.5999 4.30107 7.9999C5.00107 8.6999 5.80107 10.3999 5.90107 10.8999L6.00107 10.9999H9.00107L9.10107 10.7999C9.20107 10.2999 10.0011 8.5999 10.7011 7.8999C12.3011 6.5999 12.1011 5.0999 12.1011 4.9999V4.9999ZM9.10107 5.9999L8.60107 8.9999H8.00107V5.9999C9.10107 5.9999 8.90107 4.9999 8.90107 4.9999H6.00107V5.0999C6.00107 5.2999 6.10107 5.9999 7.00107 5.9999V8.9999H6.50107L6.30107 8.2999L6.00107 5.9999C5.30107 5.9999 5.10107 5.5999 5.00107 5.2999V4.8999C5.00107 4.0999 5.90107 3.9999 5.90107 3.9999H9.00107C9.00107 3.9999 10.0011 4.0999 10.0011 4.9999C10.0011 4.9999 10.1011 5.9999 9.10107 5.9999Z" fill="#DDB204"/>
|
||||
<path d="M10.001 5C10.001 4.1 9.00098 4 9.00098 4H5.90098C5.90098 4 5.00098 4.1 5.00098 4.9V5.3C5.00098 5.6 5.30098 6 5.90098 6L6.30098 8.3L6.50098 9H7.00098V6C6.00098 6 6.00098 5.3 6.00098 5.1V5H9.00098C9.00098 5 9.10098 6 8.10098 6V9H8.70098L9.20098 6C10.101 6 10.001 5 10.001 5Z" fill="#252526"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12C8 9.77386 9.77386 8 12 8C14.2261 8 16 9.77386 16 12C16 14.2261 14.2261 16 12 16C9.77386 16 8 14.2261 8 12Z" fill="#1E1E1E"/>
|
||||
<path d="M12.3192 12.3031L13.3495 13.3334L13.3334 13.3495L12.3031 12.3192L12 12.0162L11.697 12.3192L10.6667 13.3495L10.6506 13.3334L11.6809 12.3031L11.9839 12L11.6809 11.697L10.6506 10.6667L10.6667 10.6506L11.697 11.6809L12 11.9839L12.3031 11.6809L13.3334 10.6506L13.3495 10.6667L12.3192 11.697L12.0162 12L12.3192 12.3031ZM12 8.46034C10.03 8.46034 8.46034 10.03 8.46034 12C8.46034 13.9701 10.03 15.5397 12 15.5397C13.9701 15.5397 15.5397 13.9701 15.5397 12C15.5397 10.03 13.9701 8.46034 12 8.46034Z" fill="#007ACC" stroke="#1E1E1E" stroke-width="0.857143"/>
|
||||
<path d="M12.6225 12.0002L13.9558 13.3336L13.3336 13.9558L12.0002 12.6225L10.6669 13.9558L10.0447 13.3336L11.378 12.0002L10.0447 10.6669L10.6669 10.0447L12.0002 11.378L13.3336 10.0447L13.9558 10.6669L12.6225 12.0002Z" fill="#007ACC"/>
|
||||
<path d="M10.704 14L11.2028 12.4712L10 11.6394H11.4732L12 10L12.5361 11.6394H14L12.7972 12.4712L13.3054 14L12 13.024L10.704 14Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
10
src/vs/editor/contrib/codeAction/lightbulb-autofix.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.001 4.2C12.601 2.1 10.301 0 8.801 0H6.201C5.801 0 5.601 0.2 5.601 0.2C3.501 0.8 2.001 2.7 2.001 4.9C2.001 5.4 1.901 7.2 3.701 8.7C4.201 9.2 4.901 10.7 5.001 11.1V14.4L6.601 16H8.601L10.101 14.4V11C10.201 10.6 10.901 9.1 11.401 8.7C12.501 7.8 12.901 6.8 13.001 6V4.2Z" fill="#F6F6F6"/>
|
||||
<path d="M6.00098 12H9.00098V13H6.00098V12ZM7.00098 15H8.10098L9.00098 14H6.00098L7.00098 15Z" fill="#848484"/>
|
||||
<path d="M12.1011 4.9999C12.1011 2.6999 10.3011 0.899902 8.00107 0.899902C7.90107 0.899902 6.60107 0.999902 6.60107 0.999902C4.50107 1.2999 2.90107 2.9999 2.90107 4.9999C2.90107 5.0999 2.70107 6.5999 4.30107 7.9999C5.00107 8.6999 5.80107 10.3999 5.90107 10.8999L6.00107 10.9999H9.00107L9.10107 10.7999C9.20107 10.2999 10.0011 8.5999 10.7011 7.8999C12.3011 6.5999 12.1011 5.0999 12.1011 4.9999V4.9999ZM9.10107 5.9999L8.60107 8.9999H8.00107V5.9999C9.10107 5.9999 8.90107 4.9999 8.90107 4.9999H6.00107V5.0999C6.00107 5.2999 6.10107 5.9999 7.00107 5.9999V8.9999H6.50107L6.30107 8.2999L6.00107 5.9999C5.30107 5.9999 5.10107 5.5999 5.00107 5.2999V4.8999C5.00107 4.0999 5.90107 3.9999 5.90107 3.9999H9.00107C9.00107 3.9999 10.0011 4.0999 10.0011 4.9999C10.0011 4.9999 10.1011 5.9999 9.10107 5.9999Z" fill="#FFCC00"/>
|
||||
<path d="M10.001 5C10.001 4.1 9.00098 4 9.00098 4H5.90098C5.90098 4 5.00098 4.1 5.00098 4.9V5.3C5.00098 5.6 5.30098 6 5.90098 6L6.30098 8.3L6.50098 9H7.00098V6C6.00098 6 6.00098 5.3 6.00098 5.1V5H9.00098C9.00098 5 9.10098 6 8.10098 6V9H8.70098L9.20098 6C10.101 6 10.001 5 10.001 5Z" fill="#F0EFF1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12C8 9.77386 9.77386 8 12 8C14.2261 8 16 9.77386 16 12C16 14.2261 14.2261 16 12 16C9.77386 16 8 14.2261 8 12Z" fill="#F6F6F6"/>
|
||||
<path d="M12.3192 12.3031L13.3495 13.3334L13.3334 13.3495L12.3031 12.3192L12 12.0162L11.697 12.3192L10.6667 13.3495L10.6506 13.3334L11.6809 12.3031L11.9839 12L11.6809 11.697L10.6506 10.6667L10.6667 10.6506L11.697 11.6809L12 11.9839L12.3031 11.6809L13.3334 10.6506L13.3495 10.6667L12.3192 11.697L12.0162 12L12.3192 12.3031ZM12 8.46034C10.03 8.46034 8.46034 10.03 8.46034 12C8.46034 13.9701 10.03 15.5397 12 15.5397C13.9701 15.5397 15.5397 13.9701 15.5397 12C15.5397 10.03 13.9701 8.46034 12 8.46034Z" fill="#007ACC" stroke="#F6F6F6" stroke-width="0.857143"/>
|
||||
<path d="M12.6225 12.0002L13.9558 13.3336L13.3336 13.9558L12.0002 12.6225L10.6669 13.9558L10.0447 13.3336L11.378 12.0002L10.0447 10.6669L10.6669 10.0447L12.0002 11.378L13.3336 10.0447L13.9558 10.6669L12.6225 12.0002Z" fill="#007ACC"/>
|
||||
<path d="M10.704 14L11.2028 12.4712L10 11.6394H11.4732L12 10L12.5361 11.6394H14L12.7972 12.4712L13.3054 14L12 13.024L10.704 14Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -12,7 +12,6 @@ import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
|
||||
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
suite('CodeAction', () => {
|
||||
|
||||
@@ -117,7 +116,7 @@ suite('CodeAction', () => {
|
||||
testData.tsLint.abc
|
||||
];
|
||||
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None);
|
||||
assert.equal(actions.length, 6);
|
||||
assert.deepEqual(actions, expected);
|
||||
});
|
||||
@@ -136,20 +135,20 @@ suite('CodeAction', () => {
|
||||
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None);
|
||||
assert.equal(actions.length, 2);
|
||||
assert.strictEqual(actions[0].title, 'a');
|
||||
assert.strictEqual(actions[1].title, 'a.b');
|
||||
}
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b') } }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b') } }, CancellationToken.None);
|
||||
assert.equal(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, 'a.b');
|
||||
}
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b.c') } }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b.c') } }, CancellationToken.None);
|
||||
assert.equal(actions.length, 0);
|
||||
}
|
||||
});
|
||||
@@ -165,7 +164,7 @@ suite('CodeAction', () => {
|
||||
|
||||
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
|
||||
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None);
|
||||
assert.equal(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, 'a');
|
||||
});
|
||||
@@ -183,13 +182,13 @@ suite('CodeAction', () => {
|
||||
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None);
|
||||
assert.equal(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, 'b');
|
||||
}
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
|
||||
assert.equal(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, 'a');
|
||||
}
|
||||
@@ -208,7 +207,7 @@ suite('CodeAction', () => {
|
||||
|
||||
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
|
||||
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), {
|
||||
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
|
||||
type: 'auto',
|
||||
filter: {
|
||||
kind: CodeActionKind.QuickFix
|
||||
@@ -217,49 +216,5 @@ suite('CodeAction', () => {
|
||||
assert.strictEqual(actions.length, 0);
|
||||
assert.strictEqual(wasInvoked, false);
|
||||
});
|
||||
|
||||
test('getCodeActions requests for source actions should expand source actions range to entire document #53525', async function () {
|
||||
const provider = new class implements CodeActionProvider {
|
||||
provideCodeActions(model: ITextModel, range: Range): CodeAction[] {
|
||||
return [{
|
||||
title: rangeToString(range),
|
||||
kind: CodeActionKind.Source.value,
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
|
||||
|
||||
{
|
||||
const actions = await getCodeActions(model, new Range(1, 1, 1, 1), {
|
||||
type: 'manual',
|
||||
filter: {
|
||||
kind: CodeActionKind.Source,
|
||||
includeSourceActions: true,
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
assert.strictEqual(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, rangeToString(model.getFullModelRange()));
|
||||
}
|
||||
|
||||
{
|
||||
const range = new Range(1, 1, 1, 2);
|
||||
|
||||
// But we should not expand for non-empty selections
|
||||
const actions = await getCodeActions(model, range, {
|
||||
type: 'manual',
|
||||
filter: {
|
||||
kind: CodeActionKind.Source,
|
||||
includeSourceActions: true,
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
assert.strictEqual(actions.length, 1);
|
||||
assert.strictEqual(actions[0].title, rangeToString(range));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function rangeToString(range: Range): string {
|
||||
return `${range.startLineNumber},${range.startColumn} ${range.endLineNumber},${range.endColumn} `;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ suite('CodeAction', () => {
|
||||
assert.equal(e.trigger.type, 'auto');
|
||||
assert.ok(e.actions);
|
||||
|
||||
e.actions!.then(fixes => {
|
||||
e.actions.then(fixes => {
|
||||
oracle.dispose();
|
||||
assert.equal(fixes.length, 1);
|
||||
assert.equal(fixes.actions.length, 1);
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
@@ -88,9 +88,9 @@ suite('CodeAction', () => {
|
||||
const oracle = new CodeActionOracle(editor, markerService, (e: CodeActionsState.Triggered) => {
|
||||
assert.equal(e.trigger.type, 'auto');
|
||||
assert.ok(e.actions);
|
||||
e.actions!.then(fixes => {
|
||||
e.actions.then(fixes => {
|
||||
oracle.dispose();
|
||||
assert.equal(fixes.length, 1);
|
||||
assert.equal(fixes.actions.length, 1);
|
||||
resolve(undefined);
|
||||
}, reject);
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
private _detectVisibleLenses: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
private _editor: editorBrowser.ICodeEditor,
|
||||
private readonly _editor: editorBrowser.ICodeEditor,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@INotificationService private readonly _notificationService: INotificationService
|
||||
) {
|
||||
@@ -309,13 +309,14 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
|
||||
const resolvedSymbols = new Array<ICodeLensSymbol | undefined | null>(request.length);
|
||||
const promises = request.map((request, i) => {
|
||||
if (typeof request.provider.resolveCodeLens === 'function') {
|
||||
if (!request.symbol.command && typeof request.provider.resolveCodeLens === 'function') {
|
||||
return Promise.resolve(request.provider.resolveCodeLens(model, request.symbol, token)).then(symbol => {
|
||||
resolvedSymbols[i] = symbol;
|
||||
});
|
||||
} else {
|
||||
resolvedSymbols[i] = request.symbol;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
resolvedSymbols[i] = request.symbol;
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
|
||||
@@ -26,7 +26,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone {
|
||||
afterLineNumber: number;
|
||||
|
||||
private _lastHeight: number;
|
||||
private _onHeight: Function;
|
||||
private readonly _onHeight: Function;
|
||||
|
||||
constructor(afterLineNumber: number, onHeight: Function) {
|
||||
this.afterLineNumber = afterLineNumber;
|
||||
@@ -99,7 +99,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
this._commands = Object.create(null);
|
||||
const symbols = coalesce(inSymbols);
|
||||
if (isFalsyOrEmpty(symbols)) {
|
||||
this._domNode.innerHTML = 'no commands';
|
||||
this._domNode.innerHTML = '<span>no commands</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -164,9 +164,9 @@ export interface IDecorationIdCallback {
|
||||
|
||||
export class CodeLensHelper {
|
||||
|
||||
private _removeDecorations: string[];
|
||||
private _addDecorations: IModelDeltaDecoration[];
|
||||
private _addDecorationsCallbacks: IDecorationIdCallback[];
|
||||
private readonly _removeDecorations: string[];
|
||||
private readonly _addDecorations: IModelDeltaDecoration[];
|
||||
private readonly _addDecorationsCallbacks: IDecorationIdCallback[];
|
||||
|
||||
constructor() {
|
||||
this._removeDecorations = [];
|
||||
@@ -290,6 +290,13 @@ export class CodeLens {
|
||||
|
||||
updateCommands(symbols: Array<ICodeLensSymbol | undefined | null>): void {
|
||||
this._contentWidget.withCommands(symbols);
|
||||
for (let i = 0; i < this._data.length; i++) {
|
||||
const resolved = symbols[i];
|
||||
if (resolved) {
|
||||
const { symbol } = this._data[i];
|
||||
symbol.command = resolved.command || symbol.command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHeight(): void {
|
||||
|
||||
@@ -37,11 +37,11 @@ export class ColorDetector implements IEditorContribution {
|
||||
private _colorDatas = new Map<string, IColorData>();
|
||||
|
||||
private _colorDecoratorIds: string[] = [];
|
||||
private _decorationsTypes: { [key: string]: boolean } = {};
|
||||
private readonly _decorationsTypes: { [key: string]: boolean } = {};
|
||||
|
||||
private _isEnabled: boolean;
|
||||
|
||||
constructor(private _editor: ICodeEditor,
|
||||
constructor(private readonly _editor: ICodeEditor,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
|
||||
@@ -19,11 +19,11 @@ const $ = dom.$;
|
||||
|
||||
export class ColorPickerHeader extends Disposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private pickedColorNode: HTMLElement;
|
||||
private readonly domNode: HTMLElement;
|
||||
private readonly pickedColorNode: HTMLElement;
|
||||
private backgroundColor: Color;
|
||||
|
||||
constructor(container: HTMLElement, private model: ColorPickerModel, themeService: IThemeService) {
|
||||
constructor(container: HTMLElement, private readonly model: ColorPickerModel, themeService: IThemeService) {
|
||||
super();
|
||||
|
||||
this.domNode = $('.colorpicker-header');
|
||||
@@ -63,12 +63,12 @@ export class ColorPickerHeader extends Disposable {
|
||||
|
||||
export class ColorPickerBody extends Disposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private saturationBox: SaturationBox;
|
||||
private hueStrip: Strip;
|
||||
private opacityStrip: Strip;
|
||||
private readonly domNode: HTMLElement;
|
||||
private readonly saturationBox: SaturationBox;
|
||||
private readonly hueStrip: Strip;
|
||||
private readonly opacityStrip: Strip;
|
||||
|
||||
constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) {
|
||||
constructor(container: HTMLElement, private readonly model: ColorPickerModel, private pixelRatio: number) {
|
||||
super();
|
||||
|
||||
this.domNode = $('.colorpicker-body');
|
||||
@@ -120,9 +120,9 @@ export class ColorPickerBody extends Disposable {
|
||||
|
||||
class SaturationBox extends Disposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private selection: HTMLElement;
|
||||
private canvas: HTMLCanvasElement;
|
||||
private readonly domNode: HTMLElement;
|
||||
private readonly selection: HTMLElement;
|
||||
private readonly canvas: HTMLCanvasElement;
|
||||
private width: number;
|
||||
private height: number;
|
||||
|
||||
@@ -133,7 +133,7 @@ class SaturationBox extends Disposable {
|
||||
private _onColorFlushed = new Emitter<void>();
|
||||
readonly onColorFlushed: Event<void> = this._onColorFlushed.event;
|
||||
|
||||
constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) {
|
||||
constructor(container: HTMLElement, private readonly model: ColorPickerModel, private pixelRatio: number) {
|
||||
super();
|
||||
|
||||
this.domNode = $('.saturation-wrap');
|
||||
@@ -335,7 +335,7 @@ export class ColorPickerWidget extends Widget {
|
||||
|
||||
body: ColorPickerBody;
|
||||
|
||||
constructor(container: Node, private model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) {
|
||||
constructor(container: Node, private readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) {
|
||||
super();
|
||||
|
||||
this._register(onDidChangeZoomLevel(() => this.layout()));
|
||||
|
||||
@@ -14,7 +14,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
|
||||
|
||||
export class BlockCommentCommand implements editorCommon.ICommand {
|
||||
|
||||
private _selection: Selection;
|
||||
private readonly _selection: Selection;
|
||||
private _usedEndToken: string | null;
|
||||
|
||||
constructor(selection: Selection) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
|
||||
|
||||
abstract class CommentLineAction extends EditorAction {
|
||||
|
||||
private _type: Type;
|
||||
private readonly _type: Type;
|
||||
|
||||
constructor(type: Type, opts: IActionOptions) {
|
||||
super(opts);
|
||||
|
||||
@@ -48,12 +48,12 @@ export const enum Type {
|
||||
|
||||
export class LineCommentCommand implements editorCommon.ICommand {
|
||||
|
||||
private _selection: Selection;
|
||||
private readonly _selection: Selection;
|
||||
private _selectionId: string;
|
||||
private _deltaColumn: number;
|
||||
private _moveEndPositionDown: boolean;
|
||||
private _tabSize: number;
|
||||
private _type: Type;
|
||||
private readonly _tabSize: number;
|
||||
private readonly _type: Type;
|
||||
|
||||
constructor(selection: Selection, tabSize: number, type: Type) {
|
||||
this._selection = selection;
|
||||
|
||||
@@ -32,7 +32,7 @@ export class ContextMenuController implements IEditorContribution {
|
||||
|
||||
private _toDispose: IDisposable[] = [];
|
||||
private _contextMenuIsBeingShownCount: number = 0;
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@@ -190,7 +190,7 @@ export class ContextMenuController implements IEditorContribution {
|
||||
},
|
||||
|
||||
getKeyBinding: (action): ResolvedKeybinding | undefined => {
|
||||
return this._keybindingFor(action) || undefined;
|
||||
return this._keybindingFor(action);
|
||||
},
|
||||
|
||||
onHide: (wasCancelled: boolean) => {
|
||||
|
||||
@@ -32,12 +32,12 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.dragAndDrop';
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _toUnhook: IDisposable[];
|
||||
private _dragSelection: Selection | null;
|
||||
private _dndDecorationIds: string[];
|
||||
private _mouseDown: boolean;
|
||||
private _modiferPressed: boolean;
|
||||
private _modifierPressed: boolean;
|
||||
static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl;
|
||||
|
||||
static get(editor: ICodeEditor): DragAndDropController {
|
||||
@@ -56,7 +56,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
this._toUnhook.push(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
|
||||
this._dndDecorationIds = [];
|
||||
this._mouseDown = false;
|
||||
this._modiferPressed = false;
|
||||
this._modifierPressed = false;
|
||||
this._dragSelection = null;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
this._removeDecoration();
|
||||
this._dragSelection = null;
|
||||
this._mouseDown = false;
|
||||
this._modiferPressed = false;
|
||||
this._modifierPressed = false;
|
||||
}
|
||||
|
||||
private onEditorKeyDown(e: IKeyboardEvent): void {
|
||||
@@ -73,7 +73,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
if (hasTriggerModifier(e)) {
|
||||
this._modiferPressed = true;
|
||||
this._modifierPressed = true;
|
||||
}
|
||||
|
||||
if (this._mouseDown && hasTriggerModifier(e)) {
|
||||
@@ -89,7 +89,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
if (hasTriggerModifier(e)) {
|
||||
this._modiferPressed = false;
|
||||
this._modifierPressed = false;
|
||||
}
|
||||
|
||||
if (this._mouseDown && e.keyCode === DragAndDropController.TRIGGER_KEY_VALUE) {
|
||||
@@ -170,13 +170,13 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
(
|
||||
(
|
||||
hasTriggerModifier(mouseEvent.event) ||
|
||||
this._modiferPressed
|
||||
this._modifierPressed
|
||||
) && (
|
||||
this._dragSelection.getEndPosition().equals(newCursorPosition) || this._dragSelection.getStartPosition().equals(newCursorPosition)
|
||||
) // we allow users to paste content beside the selection
|
||||
)) {
|
||||
this._editor.pushUndoStop();
|
||||
this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, hasTriggerModifier(mouseEvent.event) || this._modiferPressed));
|
||||
this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, hasTriggerModifier(mouseEvent.event) || this._modifierPressed));
|
||||
this._editor.pushUndoStop();
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
|
||||
this._removeDecoration();
|
||||
this._dragSelection = null;
|
||||
this._mouseDown = false;
|
||||
this._modiferPressed = false;
|
||||
this._modifierPressed = false;
|
||||
this._toUnhook = dispose(this._toUnhook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export class DragAndDropCommand implements editorCommon.ICommand {
|
||||
|
||||
private selection: Selection;
|
||||
private targetPosition: Position;
|
||||
private readonly selection: Selection;
|
||||
private readonly targetPosition: Position;
|
||||
private targetSelection: Selection;
|
||||
private copy: boolean;
|
||||
private readonly copy: boolean;
|
||||
|
||||
constructor(selection: Selection, targetPosition: Position, copy: boolean) {
|
||||
this.selection = selection;
|
||||
|
||||
@@ -3,49 +3,28 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration {
|
||||
.monaco-list .monaco-list-row.focused.selected .outline-element .monaco-highlighted-label,
|
||||
.monaco-list .monaco-list-row.focused.selected .outline-element-decoration {
|
||||
/* make sure selection color wins when a label is being selected */
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element {
|
||||
.monaco-list .outline-element {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-icon {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-label {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
.monaco-list .outline-element .monaco-highlighted-label {
|
||||
color: var(--outline-element-color);
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-label .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-detail {
|
||||
visibility: hidden;
|
||||
flex: 1;
|
||||
flex-basis: 10%;
|
||||
opacity: 0.8;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 90%;
|
||||
padding-left: 4px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail {
|
||||
visibility: inherit;
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-decoration {
|
||||
.monaco-list .outline-element .outline-element-decoration {
|
||||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
@@ -55,7 +34,7 @@
|
||||
color: var(--outline-element-color);
|
||||
}
|
||||
|
||||
.monaco-tree .outline-element .outline-element-decoration.bubble {
|
||||
.monaco-list .outline-element .outline-element-decoration.bubble {
|
||||
font-family: octicons;
|
||||
font-size: 14px;
|
||||
opacity: 0.4;
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .symbol-icon {
|
||||
.monaco-workbench .symbol-icon.inline {
|
||||
background-position: left center;
|
||||
padding-left: 20px;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.monaco-workbench .symbol-icon.block {
|
||||
display: inline-block;
|
||||
height: 14px;
|
||||
width: 16px;
|
||||
|
||||
18
src/vs/editor/contrib/documentSymbols/outline.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const OutlineViewId = 'outline';
|
||||
|
||||
export const OutlineViewFiltered = new RawContextKey('outlineFiltered', false);
|
||||
export const OutlineViewFocused = new RawContextKey('outlineFocused', false);
|
||||
|
||||
export const enum OutlineConfigKeys {
|
||||
'icons' = 'outline.icons',
|
||||
'problemsEnabled' = 'outline.problems.enabled',
|
||||
'problemsColors' = 'outline.problems.colors',
|
||||
'problemsBadges' = 'outline.problems.badges'
|
||||
}
|
||||
@@ -3,18 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { binarySearch, coalesceInPlace } from 'vs/base/common/arrays';
|
||||
import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { first, forEach, size } from 'vs/base/common/collections';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { commonPrefixLength } from 'vs/base/common/strings';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
|
||||
export abstract class TreeElement {
|
||||
|
||||
@@ -87,10 +86,17 @@ export abstract class TreeElement {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IOutlineMarker {
|
||||
startLineNumber: number;
|
||||
startColumn: number;
|
||||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
severity: MarkerSeverity;
|
||||
}
|
||||
|
||||
export class OutlineElement extends TreeElement {
|
||||
|
||||
children: { [id: string]: OutlineElement; } = Object.create(null);
|
||||
score: FuzzyScore | undefined = FuzzyScore.Default;
|
||||
marker: { count: number, topSev: MarkerSeverity } | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -127,33 +133,6 @@ export class OutlineGroup extends TreeElement {
|
||||
return res;
|
||||
}
|
||||
|
||||
updateMatches(pattern: string, topMatch: OutlineElement | undefined): OutlineElement | undefined {
|
||||
for (const key in this.children) {
|
||||
topMatch = this._updateMatches(pattern, this.children[key], topMatch);
|
||||
}
|
||||
return topMatch;
|
||||
}
|
||||
|
||||
private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement | undefined): OutlineElement | undefined {
|
||||
|
||||
item.score = pattern
|
||||
? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true)
|
||||
: FuzzyScore.Default;
|
||||
|
||||
if (item.score && (!topMatch || !topMatch.score || item.score[0] > topMatch.score[0])) {
|
||||
topMatch = item;
|
||||
}
|
||||
for (const key in item.children) {
|
||||
let child = item.children[key];
|
||||
topMatch = this._updateMatches(pattern, child, topMatch);
|
||||
if (!item.score && child.score) {
|
||||
// don't filter parents with unfiltered children
|
||||
item.score = FuzzyScore.Default;
|
||||
}
|
||||
}
|
||||
return topMatch;
|
||||
}
|
||||
|
||||
getItemEnclosingPosition(position: IPosition): OutlineElement | undefined {
|
||||
return position ? this._getItemEnclosingPosition(position, this.children) : undefined;
|
||||
}
|
||||
@@ -169,13 +148,13 @@ export class OutlineGroup extends TreeElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
updateMarker(marker: IMarker[]): void {
|
||||
updateMarker(marker: IOutlineMarker[]): void {
|
||||
for (const key in this.children) {
|
||||
this._updateMarker(marker, this.children[key]);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateMarker(markers: IMarker[], item: OutlineElement): void {
|
||||
private _updateMarker(markers: IOutlineMarker[], item: OutlineElement): void {
|
||||
item.marker = undefined;
|
||||
|
||||
// find the proper start index to check for item/marker overlap.
|
||||
@@ -190,7 +169,7 @@ export class OutlineGroup extends TreeElement {
|
||||
start = idx;
|
||||
}
|
||||
|
||||
let myMarkers: IMarker[] = [];
|
||||
let myMarkers: IOutlineMarker[] = [];
|
||||
let myTopSev: MarkerSeverity | undefined;
|
||||
|
||||
for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) {
|
||||
@@ -198,7 +177,7 @@ export class OutlineGroup extends TreeElement {
|
||||
// and store them in a 'private' array.
|
||||
let marker = markers[start];
|
||||
myMarkers.push(marker);
|
||||
(markers as Array<IMarker | undefined>)[start] = undefined;
|
||||
(markers as Array<IOutlineMarker | undefined>)[start] = undefined;
|
||||
if (!myTopSev || marker.severity > myTopSev) {
|
||||
myTopSev = marker.severity;
|
||||
}
|
||||
@@ -268,7 +247,7 @@ export class OutlineModel extends TreeElement {
|
||||
|
||||
if (data!.model) {
|
||||
// resolved -> return data
|
||||
return Promise.resolve(data.model);
|
||||
return Promise.resolve(data.model!);
|
||||
}
|
||||
|
||||
// increase usage counter
|
||||
@@ -295,13 +274,17 @@ export class OutlineModel extends TreeElement {
|
||||
|
||||
static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
|
||||
|
||||
let result = new OutlineModel(textModel);
|
||||
let promises = DocumentSymbolProviderRegistry.ordered(textModel).map((provider, index) => {
|
||||
const chainedCancellation = new CancellationTokenSource();
|
||||
token.onCancellationRequested(() => chainedCancellation.cancel());
|
||||
|
||||
const result = new OutlineModel(textModel);
|
||||
const provider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
const promises = provider.map((provider, index) => {
|
||||
|
||||
let id = TreeElement.findId(`provider_${index}`, result);
|
||||
let group = new OutlineGroup(id, result, provider, index);
|
||||
|
||||
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, token)).then(result => {
|
||||
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, chainedCancellation.token)).then(result => {
|
||||
for (const info of result || []) {
|
||||
OutlineModel._makeOutlineElement(info, group);
|
||||
}
|
||||
@@ -318,7 +301,22 @@ export class OutlineModel extends TreeElement {
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => result._compact());
|
||||
const listener = DocumentSymbolProviderRegistry.onDidChange(() => {
|
||||
const newProvider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
if (!equals(newProvider, provider)) {
|
||||
chainedCancellation.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
if (chainedCancellation.token.isCancellationRequested && !token.isCancellationRequested) {
|
||||
return OutlineModel._create(textModel, token);
|
||||
} else {
|
||||
return result._compact();
|
||||
}
|
||||
}).finally(() => {
|
||||
listener.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void {
|
||||
@@ -395,20 +393,6 @@ export class OutlineModel extends TreeElement {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _matches: [string, OutlineElement | undefined];
|
||||
|
||||
updateMatches(pattern: string): OutlineElement | undefined {
|
||||
if (this._matches && this._matches[0] === pattern) {
|
||||
return this._matches[1];
|
||||
}
|
||||
let topMatch: OutlineElement | undefined;
|
||||
for (const key in this._groups) {
|
||||
topMatch = this._groups[key].updateMatches(pattern, topMatch);
|
||||
}
|
||||
this._matches = [pattern, topMatch];
|
||||
return topMatch;
|
||||
}
|
||||
|
||||
getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined {
|
||||
|
||||
let preferredGroup: OutlineGroup | undefined;
|
||||
@@ -437,7 +421,7 @@ export class OutlineModel extends TreeElement {
|
||||
return TreeElement.getElementById(id, this);
|
||||
}
|
||||
|
||||
updateMarker(marker: IMarker[]): void {
|
||||
updateMarker(marker: IOutlineMarker[]): void {
|
||||
// sort markers by start range so that we can use
|
||||
// outline element starts for quicker look up
|
||||
marker.sort(Range.compareRangesUsingStarts);
|
||||
|
||||
@@ -4,178 +4,141 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { createMatches } from 'vs/base/common/filters';
|
||||
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
|
||||
import 'vs/css!./media/outlineTree';
|
||||
import 'vs/css!./media/symbol-icons';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline';
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
export const enum OutlineItemCompareType {
|
||||
ByPosition,
|
||||
ByName,
|
||||
ByKind
|
||||
}
|
||||
export type OutlineItem = OutlineGroup | OutlineElement;
|
||||
|
||||
export class OutlineItemComparator implements ISorter {
|
||||
export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider<OutlineItem> {
|
||||
|
||||
constructor(
|
||||
public type: OutlineItemCompareType = OutlineItemCompareType.ByPosition
|
||||
) { }
|
||||
constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { }
|
||||
|
||||
compare(tree: ITree, a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): number {
|
||||
|
||||
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
|
||||
return a.providerIndex - b.providerIndex;
|
||||
getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } {
|
||||
if (element instanceof OutlineGroup) {
|
||||
return element.provider.displayName || element.id;
|
||||
} else {
|
||||
return element.symbol.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof OutlineElement && b instanceof OutlineElement) {
|
||||
switch (this.type) {
|
||||
case OutlineItemCompareType.ByKind:
|
||||
return a.symbol.kind - b.symbol.kind;
|
||||
case OutlineItemCompareType.ByName:
|
||||
return a.symbol.name.localeCompare(b.symbol.name);
|
||||
case OutlineItemCompareType.ByPosition:
|
||||
default:
|
||||
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
|
||||
return this._keybindingService.mightProducePrintableCharacter(event);
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineItemFilter implements IFilter {
|
||||
|
||||
enabled: boolean = true;
|
||||
|
||||
isVisible(tree: ITree, element: OutlineElement | any): boolean {
|
||||
if (!this.enabled) {
|
||||
return true;
|
||||
}
|
||||
return !(element instanceof OutlineElement) || Boolean(element.score);
|
||||
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
|
||||
getId(element: TreeElement): { toString(): string; } {
|
||||
return element.id;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineDataSource implements IDataSource {
|
||||
export class OutlineGroupTemplate {
|
||||
static id = 'OutlineGroupTemplate';
|
||||
|
||||
// this is a workaround for the tree showing twisties for items
|
||||
// with only filtered children
|
||||
filterOnScore: boolean = true;
|
||||
|
||||
getId(tree: ITree, element: TreeElement): string {
|
||||
return element ? element.id : 'empty';
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, element: OutlineModel | OutlineGroup | OutlineElement): boolean {
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
if (element instanceof OutlineModel) {
|
||||
return true;
|
||||
}
|
||||
if (element instanceof OutlineElement && (this.filterOnScore && !element.score)) {
|
||||
return false;
|
||||
}
|
||||
for (const id in element.children) {
|
||||
if (!this.filterOnScore || element.children[id].score) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: TreeElement): Promise<TreeElement[]> {
|
||||
let res = values(element.children);
|
||||
// console.log(element.id + ' with children ' + res.length);
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: TreeElement | any): Promise<TreeElement> {
|
||||
return Promise.resolve(element && element.parent);
|
||||
}
|
||||
|
||||
shouldAutoexpand(tree: ITree, element: TreeElement): boolean {
|
||||
return element && (element instanceof OutlineModel || element.parent instanceof OutlineModel || element instanceof OutlineGroup || element.parent instanceof OutlineGroup);
|
||||
}
|
||||
}
|
||||
|
||||
export interface OutlineTemplate {
|
||||
labelContainer: HTMLElement;
|
||||
label: HighlightedLabel;
|
||||
icon?: HTMLElement;
|
||||
detail?: HTMLElement;
|
||||
decoration?: HTMLElement;
|
||||
}
|
||||
|
||||
export class OutlineRenderer implements IRenderer {
|
||||
export class OutlineElementTemplate {
|
||||
static id = 'OutlineElementTemplate';
|
||||
container: HTMLElement;
|
||||
iconLabel: IconLabel;
|
||||
decoration: HTMLElement;
|
||||
}
|
||||
|
||||
renderProblemColors = true;
|
||||
renderProblemBadges = true;
|
||||
export class OutlineVirtualDelegate implements IListVirtualDelegate<OutlineItem> {
|
||||
|
||||
constructor(
|
||||
@IThemeService readonly _themeService: IThemeService,
|
||||
@IConfigurationService readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
getHeight(_element: OutlineItem): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(tree: ITree, element: OutlineGroup | OutlineElement): string {
|
||||
return element instanceof OutlineGroup ? 'outline-group' : 'outline-element';
|
||||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): OutlineTemplate {
|
||||
if (templateId === 'outline-element') {
|
||||
const icon = dom.$('.outline-element-icon symbol-icon');
|
||||
const labelContainer = dom.$('.outline-element-label');
|
||||
const detail = dom.$('.outline-element-detail');
|
||||
const decoration = dom.$('.outline-element-decoration');
|
||||
dom.addClass(container, 'outline-element');
|
||||
dom.append(container, icon, labelContainer, detail, decoration);
|
||||
return { icon, labelContainer, label: new HighlightedLabel(labelContainer, true), detail, decoration };
|
||||
}
|
||||
if (templateId === 'outline-group') {
|
||||
const labelContainer = dom.$('.outline-element-label');
|
||||
dom.addClass(container, 'outline-element');
|
||||
dom.append(container, labelContainer);
|
||||
return { labelContainer, label: new HighlightedLabel(labelContainer, true) };
|
||||
}
|
||||
|
||||
throw new Error(templateId);
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: OutlineGroup | OutlineElement, templateId: string, template: OutlineTemplate): void {
|
||||
if (element instanceof OutlineElement) {
|
||||
template.icon.className = `outline-element-icon ${symbolKindToCssClass(element.symbol.kind)}`;
|
||||
template.label.set(element.symbol.name, element.score ? createMatches(element.score) : undefined, localize('title.template', "{0} ({1})", element.symbol.name, OutlineRenderer._symbolKindNames[element.symbol.kind]));
|
||||
template.detail.innerText = element.symbol.detail || '';
|
||||
this._renderMarkerInfo(element, template);
|
||||
|
||||
}
|
||||
getTemplateId(element: OutlineItem): string {
|
||||
if (element instanceof OutlineGroup) {
|
||||
template.label.set(element.provider.displayName || localize('provider', "Outline Provider"));
|
||||
return OutlineGroupTemplate.id;
|
||||
} else {
|
||||
return OutlineElementTemplate.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _renderMarkerInfo(element: OutlineElement, template: OutlineTemplate): void {
|
||||
export class OutlineGroupRenderer implements ITreeRenderer<OutlineGroup, FuzzyScore, OutlineGroupTemplate> {
|
||||
|
||||
readonly templateId: string = OutlineGroupTemplate.id;
|
||||
|
||||
renderTemplate(container: HTMLElement): OutlineGroupTemplate {
|
||||
const labelContainer = dom.$('.outline-element-label');
|
||||
dom.addClass(container, 'outline-element');
|
||||
dom.append(container, labelContainer);
|
||||
return { labelContainer, label: new HighlightedLabel(labelContainer, true) };
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<OutlineGroup, FuzzyScore>, index: number, template: OutlineGroupTemplate): void {
|
||||
template.label.set(
|
||||
node.element.provider.displayName || localize('provider', "Outline Provider"),
|
||||
createMatches(node.filterData)
|
||||
);
|
||||
}
|
||||
|
||||
disposeTemplate(_template: OutlineGroupTemplate): void {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, FuzzyScore, OutlineElementTemplate> {
|
||||
|
||||
readonly templateId: string = OutlineElementTemplate.id;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): OutlineElementTemplate {
|
||||
dom.addClass(container, 'outline-element');
|
||||
const iconLabel = new IconLabel(container, { supportHighlights: true });
|
||||
const decoration = dom.$('.outline-element-decoration');
|
||||
container.appendChild(decoration);
|
||||
return { container, iconLabel, decoration };
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<OutlineElement, FuzzyScore>, index: number, template: OutlineElementTemplate): void {
|
||||
const { element } = node;
|
||||
const options = {
|
||||
matches: createMatches(node.filterData),
|
||||
labelEscapeNewLines: true,
|
||||
extraClasses: <string[]>[],
|
||||
title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind])
|
||||
};
|
||||
if (this._configurationService.getValue(OutlineConfigKeys.icons)) {
|
||||
// add styles for the icons
|
||||
options.extraClasses.push(`outline-element-icon ${symbolKindToCssClass(element.symbol.kind, true)}`);
|
||||
}
|
||||
template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options);
|
||||
this._renderMarkerInfo(element, template);
|
||||
}
|
||||
|
||||
private _renderMarkerInfo(element: OutlineElement, template: OutlineElementTemplate): void {
|
||||
|
||||
if (!element.marker) {
|
||||
dom.hide(template.decoration);
|
||||
template.labelContainer.style.removeProperty('--outline-element-color');
|
||||
template.container.style.removeProperty('--outline-element-color');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,14 +147,14 @@ export class OutlineRenderer implements IRenderer {
|
||||
const cssColor = color ? color.toString() : 'inherit';
|
||||
|
||||
// color of the label
|
||||
if (this.renderProblemColors) {
|
||||
template.labelContainer.style.setProperty('--outline-element-color', cssColor);
|
||||
if (this._configurationService.getValue(OutlineConfigKeys.problemsColors)) {
|
||||
template.container.style.setProperty('--outline-element-color', cssColor);
|
||||
} else {
|
||||
template.labelContainer.style.removeProperty('--outline-element-color');
|
||||
template.container.style.removeProperty('--outline-element-color');
|
||||
}
|
||||
|
||||
// badge with color/rollup
|
||||
if (!this.renderProblemBadges) {
|
||||
if (!this._configurationService.getValue(OutlineConfigKeys.problemsBadges)) {
|
||||
dom.hide(template.decoration);
|
||||
|
||||
} else if (count > 0) {
|
||||
@@ -239,77 +202,46 @@ export class OutlineRenderer implements IRenderer {
|
||||
[SymbolKind.Variable]: localize('Variable', "variable"),
|
||||
};
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, template: OutlineTemplate): void {
|
||||
// noop
|
||||
disposeTemplate(_template: OutlineElementTemplate): void {
|
||||
_template.iconLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineTreeState {
|
||||
export const enum OutlineSortOrder {
|
||||
ByPosition,
|
||||
ByName,
|
||||
ByKind
|
||||
}
|
||||
|
||||
readonly selected: string;
|
||||
readonly focused: string;
|
||||
readonly expanded: string[];
|
||||
export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
|
||||
|
||||
static capture(tree: ITree): OutlineTreeState {
|
||||
// selection
|
||||
let selected: string;
|
||||
let element = tree.getSelection()[0];
|
||||
if (element instanceof TreeElement) {
|
||||
selected = element.id;
|
||||
}
|
||||
constructor(
|
||||
public type: OutlineSortOrder = OutlineSortOrder.ByPosition
|
||||
) { }
|
||||
|
||||
// focus
|
||||
let focused: string;
|
||||
element = tree.getFocus(true);
|
||||
if (element instanceof TreeElement) {
|
||||
focused = element.id;
|
||||
}
|
||||
compare(a: OutlineItem, b: OutlineItem): number {
|
||||
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
|
||||
return a.providerIndex - b.providerIndex;
|
||||
|
||||
// expansion
|
||||
let expanded = new Array<string>();
|
||||
let nav = tree.getNavigator();
|
||||
while (nav.next()) {
|
||||
let element = nav.current();
|
||||
if (element instanceof TreeElement) {
|
||||
if (tree.isExpanded(element)) {
|
||||
expanded.push(element.id);
|
||||
}
|
||||
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
|
||||
if (this.type === OutlineSortOrder.ByKind) {
|
||||
return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name);
|
||||
} else if (this.type === OutlineSortOrder.ByName) {
|
||||
return a.symbol.name.localeCompare(b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
|
||||
} else if (this.type === OutlineSortOrder.ByPosition) {
|
||||
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name);
|
||||
}
|
||||
}
|
||||
return { selected, focused, expanded };
|
||||
}
|
||||
|
||||
static async restore(tree: ITree, state: OutlineTreeState, eventPayload: any): Promise<void> {
|
||||
let model = <OutlineModel>tree.getInput();
|
||||
if (!state || !(model instanceof OutlineModel)) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// expansion
|
||||
let items: TreeElement[] = [];
|
||||
for (const id of state.expanded) {
|
||||
let item = model.getItemById(id);
|
||||
if (item) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
await tree.collapseAll(undefined);
|
||||
await tree.expandAll(items);
|
||||
|
||||
// selection & focus
|
||||
let selected = model.getItemById(state.selected);
|
||||
let focused = model.getItemById(state.focused);
|
||||
tree.setSelection([selected], eventPayload);
|
||||
tree.setFocus(focused, eventPayload);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlineController extends WorkbenchTreeController {
|
||||
protected shouldToggleExpansion(element: any, event: IMouseEvent, origin: string): boolean {
|
||||
if (element instanceof OutlineElement) {
|
||||
return this.isClickOnTwistie(event);
|
||||
} else {
|
||||
return super.shouldToggleExpansion(element, event, origin);
|
||||
export class OutlineDataSource implements IDataSource<OutlineModel, OutlineItem> {
|
||||
|
||||
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): OutlineItem[] {
|
||||
if (!element) {
|
||||
return [];
|
||||
}
|
||||
return values(element.children);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ suite('OutlineModel', function () {
|
||||
|
||||
group.updateMarker(data);
|
||||
assert.equal(data.length, 0); // all 'stolen'
|
||||
assert.equal(e0.marker.count, 1);
|
||||
assert.equal(e0.marker!.count, 1);
|
||||
assert.equal(e1.marker, undefined);
|
||||
assert.equal(e2.marker.count, 2);
|
||||
assert.equal(e2.marker!.count, 2);
|
||||
|
||||
group.updateMarker([]);
|
||||
assert.equal(e0.marker, undefined);
|
||||
@@ -127,8 +127,8 @@ suite('OutlineModel', function () {
|
||||
];
|
||||
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker.count, 0);
|
||||
assert.equal(c1.marker.count, 1);
|
||||
assert.equal(p.marker!.count, 0);
|
||||
assert.equal(c1.marker!.count, 1);
|
||||
assert.equal(c2.marker, undefined);
|
||||
|
||||
data = [
|
||||
@@ -137,18 +137,18 @@ suite('OutlineModel', function () {
|
||||
fakeMarker(new Range(7, 6, 7, 8)),
|
||||
];
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker.count, 0);
|
||||
assert.equal(c1.marker.count, 2);
|
||||
assert.equal(c2.marker.count, 1);
|
||||
assert.equal(p.marker!.count, 0);
|
||||
assert.equal(c1.marker!.count, 2);
|
||||
assert.equal(c2.marker!.count, 1);
|
||||
|
||||
data = [
|
||||
fakeMarker(new Range(1, 4, 1, 11)),
|
||||
fakeMarker(new Range(7, 6, 7, 8)),
|
||||
];
|
||||
group.updateMarker(data);
|
||||
assert.equal(p.marker.count, 1);
|
||||
assert.equal(p.marker!.count, 1);
|
||||
assert.equal(c1.marker, undefined);
|
||||
assert.equal(c2.marker.count, 1);
|
||||
assert.equal(c2.marker!.count, 1);
|
||||
});
|
||||
|
||||
test('OutlineElement - updateMarker/multiple groups', function () {
|
||||
@@ -178,9 +178,9 @@ suite('OutlineModel', function () {
|
||||
|
||||
model.updateMarker(data);
|
||||
|
||||
assert.equal(model.children['g1'].children['c1'].marker.count, 2);
|
||||
assert.equal(model.children['g2'].children['c2'].children['c2.1'].marker.count, 1);
|
||||
assert.equal(model.children['g2'].children['c2'].children['c2.2'].marker.count, 1);
|
||||
assert.equal(model.children['g1']!.children['c1'].marker!.count, 2);
|
||||
assert.equal(model.children['g2']!.children['c2'].children['c2.1'].marker!.count, 1);
|
||||
assert.equal(model.children['g2']!.children['c2'].children['c2.2'].marker!.count, 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -71,12 +71,12 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
|
||||
private static readonly ID = 'editor.contrib.findController';
|
||||
|
||||
protected _editor: ICodeEditor;
|
||||
private _findWidgetVisible: IContextKey<boolean>;
|
||||
private readonly _findWidgetVisible: IContextKey<boolean>;
|
||||
protected _state: FindReplaceState;
|
||||
protected _updateHistoryDelayer: Delayer<void>;
|
||||
private _model: FindModelBoundToEditorModel | null;
|
||||
private _storageService: IStorageService;
|
||||
private _clipboardService: IClipboardService;
|
||||
private readonly _storageService: IStorageService;
|
||||
private readonly _clipboardService: IClipboardService;
|
||||
protected readonly _contextKeyService: IContextKeyService;
|
||||
|
||||
public static get(editor: ICodeEditor): CommonFindController {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export class FindDecorations implements IDisposable {
|
||||
|
||||
private _editor: IActiveCodeEditor;
|
||||
private readonly _editor: IActiveCodeEditor;
|
||||
private _decorations: string[];
|
||||
private _overviewRulerApproximateDecorations: string[];
|
||||
private _findScopeDecorationId: string | null;
|
||||
|
||||
@@ -69,14 +69,14 @@ const RESEARCH_DELAY = 240;
|
||||
|
||||
export class FindModelBoundToEditorModel {
|
||||
|
||||
private _editor: IActiveCodeEditor;
|
||||
private _state: FindReplaceState;
|
||||
private readonly _editor: IActiveCodeEditor;
|
||||
private readonly _state: FindReplaceState;
|
||||
private _toDispose: IDisposable[];
|
||||
private _decorations: FindDecorations;
|
||||
private readonly _decorations: FindDecorations;
|
||||
private _ignoreModelContentChanged: boolean;
|
||||
private _startSearchingTimer: TimeoutTimer;
|
||||
private readonly _startSearchingTimer: TimeoutTimer;
|
||||
|
||||
private _updateDecorationsScheduler: RunOnceScheduler;
|
||||
private readonly _updateDecorationsScheduler: RunOnceScheduler;
|
||||
private _isDisposed: boolean;
|
||||
|
||||
constructor(editor: IActiveCodeEditor, state: FindReplaceState) {
|
||||
|
||||
@@ -18,14 +18,14 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
private static readonly ID = 'editor.contrib.findOptionsWidget';
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _state: FindReplaceState;
|
||||
private _keybindingService: IKeybindingService;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private readonly _state: FindReplaceState;
|
||||
private readonly _keybindingService: IKeybindingService;
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
private regex: RegexCheckbox;
|
||||
private wholeWords: WholeWordsCheckbox;
|
||||
private caseSensitive: CaseSensitiveCheckbox;
|
||||
private readonly _domNode: HTMLElement;
|
||||
private readonly regex: RegexCheckbox;
|
||||
private readonly wholeWords: WholeWordsCheckbox;
|
||||
private readonly caseSensitive: CaseSensitiveCheckbox;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@@ -46,7 +46,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this._domNode.setAttribute('role', 'presentation');
|
||||
this._domNode.setAttribute('aria-hidden', 'true');
|
||||
|
||||
const inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder) || undefined;
|
||||
const inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder);
|
||||
|
||||
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
|
||||
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand),
|
||||
@@ -179,7 +179,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder) || undefined };
|
||||
let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder) };
|
||||
this.caseSensitive.style(inputStyles);
|
||||
this.wholeWords.style(inputStyles);
|
||||
this.regex.style(inputStyles);
|
||||
|
||||
@@ -79,10 +79,6 @@
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input {
|
||||
width: 100% !important;
|
||||
padding-right: 66px;
|
||||
}
|
||||
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input,
|
||||
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
|
||||
padding-top: 2px;
|
||||
|
||||
@@ -29,7 +29,8 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
|
||||
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
export interface IFindController {
|
||||
replace(): void;
|
||||
@@ -83,8 +84,8 @@ export class FindWidgetViewZone implements IViewZone {
|
||||
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
|
||||
private static readonly ID = 'editor.contrib.findWidget';
|
||||
private readonly _codeEditor: ICodeEditor;
|
||||
private _state: FindReplaceState;
|
||||
private _controller: IFindController;
|
||||
private readonly _state: FindReplaceState;
|
||||
private readonly _controller: IFindController;
|
||||
private readonly _contextViewProvider: IContextViewProvider;
|
||||
private readonly _keybindingService: IKeybindingService;
|
||||
private readonly _contextKeyService: IContextKeyService;
|
||||
@@ -106,16 +107,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
private _isReplaceVisible: boolean;
|
||||
private _ignoreChangeEvent: boolean;
|
||||
|
||||
private _findFocusTracker: dom.IFocusTracker;
|
||||
private _findInputFocused: IContextKey<boolean>;
|
||||
private _replaceFocusTracker: dom.IFocusTracker;
|
||||
private _replaceInputFocused: IContextKey<boolean>;
|
||||
private _viewZone: FindWidgetViewZone;
|
||||
private readonly _findFocusTracker: dom.IFocusTracker;
|
||||
private readonly _findInputFocused: IContextKey<boolean>;
|
||||
private readonly _replaceFocusTracker: dom.IFocusTracker;
|
||||
private readonly _replaceInputFocused: IContextKey<boolean>;
|
||||
private _viewZone?: FindWidgetViewZone;
|
||||
private _viewZoneId?: number;
|
||||
|
||||
private _resizeSash: Sash;
|
||||
private _resized: boolean;
|
||||
private _updateHistoryDelayer: Delayer<void>;
|
||||
private readonly _updateHistoryDelayer: Delayer<void>;
|
||||
|
||||
constructor(
|
||||
codeEditor: ICodeEditor,
|
||||
@@ -160,6 +161,17 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
if (e.accessibilitySupport) {
|
||||
this.updateAccessibilitySupport();
|
||||
}
|
||||
|
||||
if (e.contribInfo) {
|
||||
const addExtraSpaceOnTop = this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop;
|
||||
if (addExtraSpaceOnTop && !this._viewZone) {
|
||||
this._viewZone = new FindWidgetViewZone(0);
|
||||
this._showViewZone();
|
||||
}
|
||||
if (!addExtraSpaceOnTop && this._viewZone) {
|
||||
this._removeViewZone();
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.updateAccessibilitySupport();
|
||||
this._register(this._codeEditor.onDidChangeCursorSelection(() => {
|
||||
@@ -197,7 +209,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
}));
|
||||
|
||||
this._codeEditor.addOverlayWidget(this);
|
||||
this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line.
|
||||
if (this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop) {
|
||||
this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line.
|
||||
}
|
||||
|
||||
this._applyTheme(themeService.getTheme());
|
||||
this._register(themeService.onThemeChange(this._applyTheme.bind(this)));
|
||||
@@ -434,7 +448,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
const startLeft = editorCoords.left + (startCoords ? startCoords.left : 0);
|
||||
const startTop = startCoords ? startCoords.top : 0;
|
||||
|
||||
if (startTop < this._viewZone.heightInPx) {
|
||||
if (this._viewZone && startTop < this._viewZone.heightInPx) {
|
||||
if (selection.endLineNumber > selection.startLineNumber) {
|
||||
adjustEditorScrollTop = false;
|
||||
}
|
||||
@@ -468,40 +482,42 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
this._codeEditor.focus();
|
||||
}
|
||||
this._codeEditor.layoutOverlayWidget(this);
|
||||
this._codeEditor.changeViewZones((accessor) => {
|
||||
if (this._viewZoneId !== undefined) {
|
||||
accessor.removeZone(this._viewZoneId);
|
||||
this._viewZoneId = undefined;
|
||||
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() - this._viewZone.heightInPx);
|
||||
}
|
||||
});
|
||||
this._removeViewZone();
|
||||
}
|
||||
}
|
||||
|
||||
private _layoutViewZone() {
|
||||
if (!this._isVisible) {
|
||||
const addExtraSpaceOnTop = this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop;
|
||||
|
||||
if (!addExtraSpaceOnTop) {
|
||||
this._removeViewZone();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._viewZoneId !== undefined) {
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
const viewZone = this._viewZone;
|
||||
if (this._viewZoneId !== undefined || !viewZone) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._codeEditor.changeViewZones((accessor) => {
|
||||
if (this._state.isReplaceRevealed) {
|
||||
this._viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
|
||||
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
|
||||
} else {
|
||||
this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
}
|
||||
|
||||
this._viewZoneId = accessor.addZone(this._viewZone);
|
||||
this._viewZoneId = accessor.addZone(viewZone);
|
||||
// scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning.
|
||||
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + this._viewZone.heightInPx);
|
||||
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx);
|
||||
});
|
||||
}
|
||||
|
||||
private _showViewZone(adjustScroll: boolean = true) {
|
||||
if (!this._isVisible) {
|
||||
const viewZone = this._viewZone;
|
||||
if (!this._isVisible || !viewZone) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -510,17 +526,17 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
|
||||
if (this._viewZoneId !== undefined) {
|
||||
if (this._state.isReplaceRevealed) {
|
||||
this._viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
|
||||
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
|
||||
scrollAdjustment = FIND_REPLACE_AREA_HEIGHT - FIND_INPUT_AREA_HEIGHT;
|
||||
} else {
|
||||
this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
scrollAdjustment = FIND_INPUT_AREA_HEIGHT - FIND_REPLACE_AREA_HEIGHT;
|
||||
}
|
||||
accessor.removeZone(this._viewZoneId);
|
||||
} else {
|
||||
this._viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
|
||||
}
|
||||
this._viewZoneId = accessor.addZone(this._viewZone);
|
||||
this._viewZoneId = accessor.addZone(viewZone);
|
||||
|
||||
if (adjustScroll) {
|
||||
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
|
||||
@@ -528,6 +544,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
});
|
||||
}
|
||||
|
||||
private _removeViewZone() {
|
||||
this._codeEditor.changeViewZones((accessor) => {
|
||||
if (this._viewZoneId !== undefined) {
|
||||
accessor.removeZone(this._viewZoneId);
|
||||
this._viewZoneId = undefined;
|
||||
if (this._viewZone) {
|
||||
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() - this._viewZone.heightInPx);
|
||||
this._viewZone = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
let inputStyles: IFindInputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
@@ -542,7 +571,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
|
||||
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
|
||||
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
|
||||
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder)
|
||||
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
|
||||
};
|
||||
this._findInput.style(inputStyles);
|
||||
this._replaceInputBox.style(inputStyles);
|
||||
@@ -825,7 +854,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._state.change({ searchScope: undefined }, true);
|
||||
this._state.change({ searchScope: null }, true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -835,7 +864,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand),
|
||||
className: 'close-fw',
|
||||
onTrigger: () => {
|
||||
this._state.change({ isRevealed: false, searchScope: undefined }, false);
|
||||
this._state.change({ isRevealed: false, searchScope: null }, false);
|
||||
},
|
||||
onKeyDown: (e) => {
|
||||
if (e.equals(KeyCode.Tab)) {
|
||||
@@ -975,7 +1004,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
|
||||
private updateAccessibilitySupport(): void {
|
||||
const value = this._codeEditor.getConfiguration().accessibilitySupport;
|
||||
this._findInput.setFocusInputOnOptionClick(value !== platform.AccessibilitySupport.Enabled);
|
||||
this._findInput.setFocusInputOnOptionClick(value !== AccessibilitySupport.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,7 +1159,7 @@ export class SimpleButton extends Widget {
|
||||
// theming
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const addBackgroundColorRule = (selector: string, color: Color | null): void => {
|
||||
const addBackgroundColorRule = (selector: string, color: Color | undefined): void => {
|
||||
if (color) {
|
||||
collector.addRule(`.monaco-editor ${selector} { background-color: ${color}; }`);
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ interface IEditOperation {
|
||||
|
||||
export class ReplaceAllCommand implements editorCommon.ICommand {
|
||||
|
||||
private _editorSelection: Selection;
|
||||
private readonly _editorSelection: Selection;
|
||||
private _trackedEditorSelectionId: string;
|
||||
private _ranges: Range[];
|
||||
private _replaceStrings: string[];
|
||||
private readonly _ranges: Range[];
|
||||
private readonly _replaceStrings: string[];
|
||||
|
||||
constructor(editorSelection: Selection, ranges: Range[], replaceStrings: string[]) {
|
||||
this._editorSelection = editorSelection;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .simple-find-part {
|
||||
visibility: hidden; /* Use visibility to maintain flex layout while hidden otherwise interferes with transition */
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
top: -45px;
|
||||
@@ -27,6 +28,10 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .simple-find-part.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.monaco-workbench .simple-find-part.visible-transition {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@@ -34,10 +39,6 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench .simple-find-part .monaco-findInput .monaco-inputbox .wrapper .input {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .simple-find-part .button {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./simpleFindWidget';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
|
||||
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
@@ -16,7 +16,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { editorWidgetBackground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
|
||||
import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
|
||||
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
|
||||
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
|
||||
@@ -25,13 +25,13 @@ const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next mat
|
||||
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
|
||||
|
||||
export abstract class SimpleFindWidget extends Widget {
|
||||
private _findInput: FindInput;
|
||||
private _domNode?: HTMLElement;
|
||||
private _innerDomNode: HTMLElement;
|
||||
private readonly _findInput: FindInput;
|
||||
private readonly _domNode: HTMLElement;
|
||||
private readonly _innerDomNode: HTMLElement;
|
||||
private _isVisible: boolean = false;
|
||||
private _focusTracker: dom.IFocusTracker;
|
||||
private _findInputFocusTracker: dom.IFocusTracker;
|
||||
private _updateHistoryDelayer: Delayer<void>;
|
||||
private readonly _focusTracker: dom.IFocusTracker;
|
||||
private readonly _findInputFocusTracker: dom.IFocusTracker;
|
||||
private readonly _updateHistoryDelayer: Delayer<void>;
|
||||
|
||||
constructor(
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||
@@ -159,7 +159,7 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
}
|
||||
|
||||
public updateTheme(theme: ITheme): void {
|
||||
const inputStyles = {
|
||||
const inputStyles: IFindInputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputBackground: theme.getColor(inputBackground),
|
||||
inputForeground: theme.getColor(inputForeground),
|
||||
@@ -182,7 +182,6 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
|
||||
if (this._domNode && this._domNode.parentElement) {
|
||||
this._domNode.parentElement.removeChild(this._domNode);
|
||||
this._domNode = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,10 +203,9 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
|
||||
setTimeout(() => {
|
||||
dom.addClass(this._innerDomNode, 'visible');
|
||||
dom.addClass(this._innerDomNode, 'visible-transition');
|
||||
this._innerDomNode.setAttribute('aria-hidden', 'false');
|
||||
setTimeout(() => {
|
||||
this._findInput.select();
|
||||
}, 200);
|
||||
this._findInput.select();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -220,16 +218,20 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
|
||||
setTimeout(() => {
|
||||
dom.addClass(this._innerDomNode, 'visible');
|
||||
dom.addClass(this._innerDomNode, 'visible-transition');
|
||||
this._innerDomNode.setAttribute('aria-hidden', 'false');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
if (this._isVisible) {
|
||||
this._isVisible = false;
|
||||
|
||||
dom.removeClass(this._innerDomNode, 'visible');
|
||||
dom.removeClass(this._innerDomNode, 'visible-transition');
|
||||
this._innerDomNode.setAttribute('aria-hidden', 'true');
|
||||
// Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list
|
||||
setTimeout(() => {
|
||||
this._isVisible = false;
|
||||
dom.removeClass(this._innerDomNode, 'visible');
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ suite('FindController', () => {
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
getInteger: (key: string) => undefined,
|
||||
getNumber: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => undefined
|
||||
} as any);
|
||||
@@ -440,7 +440,7 @@ suite('FindController query options persistence', () => {
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
getInteger: (key: string) => undefined,
|
||||
getNumber: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => undefined
|
||||
} as any);
|
||||
|
||||
@@ -1507,7 +1507,7 @@ suite('FindModel', () => {
|
||||
]
|
||||
);
|
||||
|
||||
editor.getModel().setValue('hello\nhi');
|
||||
editor!.getModel()!.setValue('hello\nhi');
|
||||
assertFindState(
|
||||
editor,
|
||||
[1, 1, 1, 1],
|
||||
@@ -1538,7 +1538,7 @@ suite('FindModel', () => {
|
||||
|
||||
findModel.selectAllMatches();
|
||||
|
||||
assert.deepEqual(editor.getSelections().map(s => s.toString()), [
|
||||
assert.deepEqual(editor!.getSelections()!.map(s => s.toString()), [
|
||||
new Selection(6, 14, 6, 19),
|
||||
new Selection(6, 27, 6, 32),
|
||||
new Selection(7, 14, 7, 19),
|
||||
@@ -1582,14 +1582,14 @@ suite('FindModel', () => {
|
||||
|
||||
findModel.selectAllMatches();
|
||||
|
||||
assert.deepEqual(editor.getSelections().map(s => s.toString()), [
|
||||
assert.deepEqual(editor!.getSelections()!.map(s => s.toString()), [
|
||||
new Selection(7, 14, 7, 19),
|
||||
new Selection(6, 14, 6, 19),
|
||||
new Selection(6, 27, 6, 32),
|
||||
new Selection(8, 14, 8, 19)
|
||||
].map(s => s.toString()));
|
||||
|
||||
assert.deepEqual(editor.getSelection().toString(), new Selection(7, 14, 7, 19).toString());
|
||||
assert.deepEqual(editor!.getSelection()!.toString(), new Selection(7, 14, 7, 19).toString());
|
||||
|
||||
assertFindState(
|
||||
editor,
|
||||
@@ -1984,7 +1984,7 @@ suite('FindModel', () => {
|
||||
for (let i = 0; i < 1100; i++) {
|
||||
initialText += 'line' + i + '\n';
|
||||
}
|
||||
editor.getModel().setValue(initialText);
|
||||
editor!.getModel()!.setValue(initialText);
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: '^', replaceString: 'a ', isRegex: true }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
@@ -1996,7 +1996,7 @@ suite('FindModel', () => {
|
||||
expectedText += 'a line' + i + '\n';
|
||||
}
|
||||
expectedText += 'a ';
|
||||
assert.equal(editor.getModel().getValue(), expectedText);
|
||||
assert.equal(editor!.getModel()!.getValue(), expectedText);
|
||||
|
||||
findModel.dispose();
|
||||
findState.dispose();
|
||||
|
||||
@@ -55,12 +55,12 @@ export class FoldingController implements IEditorContribution {
|
||||
return editor.getContribution<FoldingController>(ID);
|
||||
}
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private _isEnabled: boolean;
|
||||
private _autoHideFoldingControls: boolean;
|
||||
private _useFoldingProviders: boolean;
|
||||
|
||||
private foldingDecorationProvider: FoldingDecorationProvider;
|
||||
private readonly foldingDecorationProvider: FoldingDecorationProvider;
|
||||
|
||||
private foldingModel: FoldingModel | null;
|
||||
private hiddenRangeModel: HiddenRangeModel | null;
|
||||
@@ -92,7 +92,6 @@ export class FoldingController implements IEditorContribution {
|
||||
this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls;
|
||||
|
||||
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
|
||||
this.globalToDispose.push(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged()));
|
||||
|
||||
this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
|
||||
if (e.contribInfo) {
|
||||
@@ -193,7 +192,8 @@ export class FoldingController implements IEditorContribution {
|
||||
|
||||
this.cursorChangedScheduler = new RunOnceScheduler(() => this.revealCursor(), 200);
|
||||
this.localToDispose.push(this.cursorChangedScheduler);
|
||||
this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(() => this.onModelContentChanged())); // covers model language changes as well
|
||||
this.localToDispose.push(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged()));
|
||||
this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(() => this.onFoldingStrategyChanged())); // covers model language changes as well
|
||||
this.localToDispose.push(this.editor.onDidChangeModelContent(() => this.onModelContentChanged()));
|
||||
this.localToDispose.push(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged()));
|
||||
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
|
||||
@@ -279,6 +279,9 @@ export class FoldingController implements IEditorContribution {
|
||||
}
|
||||
return foldingModel;
|
||||
});
|
||||
}).then(undefined, (err) => {
|
||||
onUnexpectedError(err);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -519,7 +522,27 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
|
||||
* 'direction': If 'up', unfold given number of levels up otherwise unfolds down.
|
||||
* 'selectionLines': The start lines (0-based) of the editor selections to apply the unfold action to. If not set, the active selection(s) will be used.
|
||||
`,
|
||||
constraint: foldingArgumentsConstraint
|
||||
constraint: foldingArgumentsConstraint,
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'levels': {
|
||||
'type': 'number',
|
||||
'default': 1
|
||||
},
|
||||
'direction': {
|
||||
'type': 'string',
|
||||
'enum': ['up', 'down'],
|
||||
'default': 'down'
|
||||
},
|
||||
'selectionLines': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'number'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -584,7 +607,27 @@ class FoldAction extends FoldingAction<FoldingArguments> {
|
||||
* 'direction': If 'up', folds given number of levels up otherwise folds down.
|
||||
* 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used.
|
||||
`,
|
||||
constraint: foldingArgumentsConstraint
|
||||
constraint: foldingArgumentsConstraint,
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'levels': {
|
||||
'type': 'number',
|
||||
'default': 1
|
||||
},
|
||||
'direction': {
|
||||
'type': 'string',
|
||||
'enum': ['up', 'down'],
|
||||
'default': 'down'
|
||||
},
|
||||
'selectionLines': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'number'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class FoldingDecorationProvider implements IDecorationProvider {
|
||||
|
||||
public autoHideFoldingControls: boolean = true;
|
||||
|
||||
constructor(private editor: ICodeEditor) {
|
||||
constructor(private readonly editor: ICodeEditor) {
|
||||
}
|
||||
|
||||
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
|
||||
|
||||
@@ -21,8 +21,8 @@ export interface FoldingModelChangeEvent {
|
||||
export type CollapseMemento = ILineRange[];
|
||||
|
||||
export class FoldingModel {
|
||||
private _textModel: ITextModel;
|
||||
private _decorationProvider: IDecorationProvider;
|
||||
private readonly _textModel: ITextModel;
|
||||
private readonly _decorationProvider: IDecorationProvider;
|
||||
|
||||
private _regions: FoldingRegions;
|
||||
private _editorDecorationIds: string[];
|
||||
|
||||
@@ -14,11 +14,11 @@ export const MAX_LINE_NUMBER = 0xFFFFFF;
|
||||
const MASK_INDENT = 0xFF000000;
|
||||
|
||||
export class FoldingRegions {
|
||||
private _startIndexes: Uint32Array;
|
||||
private _endIndexes: Uint32Array;
|
||||
private _collapseStates: Uint32Array;
|
||||
private readonly _startIndexes: Uint32Array;
|
||||
private readonly _endIndexes: Uint32Array;
|
||||
private readonly _collapseStates: Uint32Array;
|
||||
private _parentsComputed: boolean;
|
||||
private _types: Array<string | undefined> | undefined;
|
||||
private readonly _types: Array<string | undefined> | undefined;
|
||||
|
||||
constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, types?: Array<string | undefined>) {
|
||||
if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
|
||||
@@ -154,7 +154,7 @@ export class FoldingRegions {
|
||||
|
||||
export class FoldingRegion {
|
||||
|
||||
constructor(private ranges: FoldingRegions, private index: number) {
|
||||
constructor(private readonly ranges: FoldingRegions, private index: number) {
|
||||
}
|
||||
|
||||
public get startLineNumber() {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { findFirstInSorted } from 'vs/base/common/arrays';
|
||||
|
||||
export class HiddenRangeModel {
|
||||
private _foldingModel: FoldingModel;
|
||||
private readonly _foldingModel: FoldingModel;
|
||||
private _hiddenRanges: IRange[];
|
||||
private _foldingModelListener: IDisposable | null;
|
||||
private _updateEventEmitter = new Emitter<IRange[]>();
|
||||
|
||||
@@ -20,7 +20,7 @@ export class IndentRangeProvider implements RangeProvider {
|
||||
|
||||
readonly decorations;
|
||||
|
||||
constructor(private editorModel: ITextModel) {
|
||||
constructor(private readonly editorModel: ITextModel) {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -36,11 +36,11 @@ export class IndentRangeProvider implements RangeProvider {
|
||||
|
||||
// public only for testing
|
||||
export class RangesCollector {
|
||||
private _startIndexes: number[];
|
||||
private _endIndexes: number[];
|
||||
private _indentOccurrences: number[];
|
||||
private readonly _startIndexes: number[];
|
||||
private readonly _endIndexes: number[];
|
||||
private readonly _indentOccurrences: number[];
|
||||
private _length: number;
|
||||
private _foldingRangesLimit: number;
|
||||
private readonly _foldingRangesLimit: number;
|
||||
|
||||
constructor(foldingRangesLimit: number) {
|
||||
this._startIndexes = [];
|
||||
|
||||
@@ -17,7 +17,7 @@ export class InitializingRangeProvider implements RangeProvider {
|
||||
private decorationIds: string[] | undefined;
|
||||
private timeout: any;
|
||||
|
||||
constructor(private editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) {
|
||||
constructor(private readonly editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) {
|
||||
if (initialRanges.length) {
|
||||
let toDecorationRange = (range: ILineRange): IModelDeltaDecoration => {
|
||||
return {
|
||||
|
||||
@@ -25,7 +25,7 @@ export class SyntaxRangeProvider implements RangeProvider {
|
||||
|
||||
readonly id = ID_SYNTAX_PROVIDER;
|
||||
|
||||
constructor(private editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
|
||||
constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
|
||||
}
|
||||
|
||||
compute(cancellationToken: CancellationToken): Promise<FoldingRegions | null> {
|
||||
@@ -69,13 +69,13 @@ function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextMode
|
||||
}
|
||||
|
||||
export class RangesCollector {
|
||||
private _startIndexes: number[];
|
||||
private _endIndexes: number[];
|
||||
private _nestingLevels: number[];
|
||||
private _nestingLevelCounts: number[];
|
||||
private _types: Array<string | undefined>;
|
||||
private readonly _startIndexes: number[];
|
||||
private readonly _endIndexes: number[];
|
||||
private readonly _nestingLevels: number[];
|
||||
private readonly _nestingLevelCounts: number[];
|
||||
private readonly _types: Array<string | undefined>;
|
||||
private _length: number;
|
||||
private _foldingRangesLimit: number;
|
||||
private readonly _foldingRangesLimit: number;
|
||||
|
||||
constructor(foldingRangesLimit: number) {
|
||||
this._startIndexes = [];
|
||||
|
||||
@@ -8,68 +8,168 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { first } from 'vs/base/common/async';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class NoProviderError extends Error {
|
||||
export const enum FormatMode {
|
||||
Auto = 1,
|
||||
Manual = 2,
|
||||
}
|
||||
|
||||
static is(thing: any): thing is NoProviderError {
|
||||
return thing instanceof Error && thing.name === NoProviderError._name;
|
||||
}
|
||||
export const enum FormatKind {
|
||||
Document = 8,
|
||||
Range = 16,
|
||||
OnType = 32,
|
||||
}
|
||||
|
||||
private static readonly _name = 'NOPRO';
|
||||
export interface IFormatterConflictCallback {
|
||||
(extensionIds: (ExtensionIdentifier | undefined)[], model: ITextModel, mode: number): void;
|
||||
}
|
||||
|
||||
constructor(message?: string) {
|
||||
super();
|
||||
this.name = NoProviderError._name;
|
||||
if (message) {
|
||||
this.message = message;
|
||||
let _conflictResolver: IFormatterConflictCallback | undefined;
|
||||
|
||||
export function setFormatterConflictCallback(callback: IFormatterConflictCallback): IDisposable {
|
||||
let oldCallback = _conflictResolver;
|
||||
_conflictResolver = callback;
|
||||
return {
|
||||
dispose() {
|
||||
if (oldCallback) {
|
||||
_conflictResolver = oldCallback;
|
||||
oldCallback = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function invokeFormatterCallback<T extends { extensionId?: ExtensionIdentifier }>(formatter: T[], model: ITextModel, mode: number): void {
|
||||
if (_conflictResolver) {
|
||||
const ids = formatter.map(formatter => formatter.extensionId);
|
||||
_conflictResolver(ids, model, mode);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[] | undefined | null> {
|
||||
export async function getDocumentRangeFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
range: Range,
|
||||
options: FormattingOptions,
|
||||
mode: FormatMode,
|
||||
token: CancellationToken
|
||||
): Promise<TextEdit[] | undefined | null> {
|
||||
|
||||
const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
if (providers.length === 0) {
|
||||
return Promise.reject(new NoProviderError());
|
||||
}
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'range',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: providers.length,
|
||||
extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
invokeFormatterCallback(providers, model, mode | FormatKind.Range);
|
||||
|
||||
return first(providers.map(provider => () => {
|
||||
return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token))
|
||||
.then(undefined, onUnexpectedExternalError);
|
||||
}), isNonEmptyArray);
|
||||
return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError);
|
||||
}), isNonEmptyArray).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
});
|
||||
}
|
||||
|
||||
export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[] | null | undefined> {
|
||||
const providers = DocumentFormattingEditProviderRegistry.ordered(model);
|
||||
export function getDocumentFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
options: FormattingOptions,
|
||||
mode: FormatMode,
|
||||
token: CancellationToken
|
||||
): Promise<TextEdit[] | null | undefined> {
|
||||
|
||||
const docFormattingProviders = DocumentFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'document',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: docFormattingProviders.length,
|
||||
extensions: docFormattingProviders.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
if (docFormattingProviders.length > 0) {
|
||||
return first(docFormattingProviders.map(provider => () => {
|
||||
// first with result wins...
|
||||
return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError);
|
||||
}), isNonEmptyArray).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
});
|
||||
} else {
|
||||
// try range formatters when no document formatter is registered
|
||||
return getDocumentRangeFormattingEdits(telemetryService, workerService, model, model.getFullModelRange(), options, mode | FormatKind.Document, token);
|
||||
}
|
||||
}
|
||||
|
||||
export function getOnTypeFormattingEdits(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
model: ITextModel,
|
||||
position: Position,
|
||||
ch: string,
|
||||
options: FormattingOptions
|
||||
): Promise<TextEdit[] | null | undefined> {
|
||||
|
||||
const providers = OnTypeFormattingEditProviderRegistry.ordered(model);
|
||||
|
||||
/* __GDPR__
|
||||
"formatterInfo" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('formatterInfo', {
|
||||
type: 'ontype',
|
||||
language: model.getLanguageIdentifier().language,
|
||||
count: providers.length,
|
||||
extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown')
|
||||
});
|
||||
|
||||
// try range formatters when no document formatter is registered
|
||||
if (providers.length === 0) {
|
||||
return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token);
|
||||
}
|
||||
|
||||
return first(providers.map(provider => () => {
|
||||
return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token))
|
||||
.then(undefined, onUnexpectedExternalError);
|
||||
}), isNonEmptyArray);
|
||||
}
|
||||
|
||||
export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): Promise<TextEdit[] | null | undefined> {
|
||||
const [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
|
||||
if (!support) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
if (support.autoFormatTriggerCharacters.indexOf(ch) < 0) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return Promise.resolve(support.provideOnTypeFormattingEdits(model, position, ch, options, CancellationToken.None)).then(r => r, onUnexpectedExternalError);
|
||||
if (providers[0].autoFormatTriggerCharacters.indexOf(ch) < 0) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return Promise.resolve(providers[0].provideOnTypeFormattingEdits(model, position, ch, options, CancellationToken.None)).catch(onUnexpectedExternalError).then(edits => {
|
||||
return workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
});
|
||||
}
|
||||
|
||||
registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) {
|
||||
@@ -81,7 +181,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args)
|
||||
if (!model) {
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
return getDocumentRangeFormattingEdits(model, Range.lift(range), options, CancellationToken.None);
|
||||
return getDocumentRangeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Range.lift(range), options, FormatMode.Auto, CancellationToken.None);
|
||||
});
|
||||
|
||||
registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) {
|
||||
@@ -94,13 +194,18 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
|
||||
return getDocumentFormattingEdits(model, options, CancellationToken.None);
|
||||
return getDocumentFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, options, FormatMode.Auto, CancellationToken.None);
|
||||
});
|
||||
|
||||
registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) {
|
||||
const { ch, options } = args;
|
||||
if (typeof ch !== 'string') {
|
||||
throw illegalArgument('ch');
|
||||
registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) {
|
||||
const { resource, position, ch, options } = args;
|
||||
if (!(resource instanceof URI) || !Position.isIPosition(position) || typeof ch !== 'string') {
|
||||
throw illegalArgument();
|
||||
}
|
||||
return getOnTypeFormattingEdits(model, position, ch, options);
|
||||
const model = accessor.get(IModelService).getModel(resource);
|
||||
if (!model) {
|
||||
throw illegalArgument('resource');
|
||||
}
|
||||
|
||||
return getOnTypeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -18,17 +17,15 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { getOnTypeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format';
|
||||
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format';
|
||||
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
|
||||
import * as nls from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
|
||||
function alertFormattingEdits(edits: ISingleEditOperation[]): void {
|
||||
|
||||
@@ -57,131 +54,69 @@ function alertFormattingEdits(edits: ISingleEditOperation[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
export const enum FormatRangeType {
|
||||
const enum FormatRangeType {
|
||||
Full,
|
||||
Selection,
|
||||
}
|
||||
|
||||
export function formatDocumentRange(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, rangeOrRangeType: Range | FormatRangeType, options: FormattingOptions, token: CancellationToken): Promise<void> {
|
||||
function formatDocumentRange(
|
||||
telemetryService: ITelemetryService,
|
||||
workerService: IEditorWorkerService,
|
||||
editor: IActiveCodeEditor,
|
||||
rangeOrRangeType: Range | FormatRangeType,
|
||||
options: FormattingOptions,
|
||||
token: CancellationToken
|
||||
): Promise<void> {
|
||||
|
||||
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel());
|
||||
if (provider.length === 0) {
|
||||
return Promise.reject(new NoProviderError());
|
||||
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
const model = editor.getModel();
|
||||
|
||||
let range: Range;
|
||||
if (rangeOrRangeType === FormatRangeType.Full) {
|
||||
// full
|
||||
range = model.getFullModelRange();
|
||||
|
||||
} else if (rangeOrRangeType === FormatRangeType.Selection) {
|
||||
// selection or line (when empty)
|
||||
range = editor.getSelection();
|
||||
if (range.isEmpty()) {
|
||||
range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber));
|
||||
}
|
||||
} else {
|
||||
// as is
|
||||
range = rangeOrRangeType;
|
||||
}
|
||||
|
||||
// Know how often multiple providers clash and (for now)
|
||||
// continue picking the 'first' provider
|
||||
if (provider.length !== 1) {
|
||||
/* __GDPR__
|
||||
"manyformatters" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('manyformatters', {
|
||||
type: 'range',
|
||||
language: editor.getModel().getLanguageIdentifier().language,
|
||||
count: provider.length,
|
||||
});
|
||||
provider.length = 1;
|
||||
}
|
||||
|
||||
let allEdits: ISingleEditOperation[] = [];
|
||||
|
||||
editor.pushUndoStop();
|
||||
return sequence(provider.map(provider => {
|
||||
// create a formatting task per provider. they run sequentially,
|
||||
// potentially undoing the working of a previous formatter
|
||||
return () => {
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
const model = editor.getModel();
|
||||
|
||||
let range: Range;
|
||||
if (rangeOrRangeType === FormatRangeType.Full) {
|
||||
// full
|
||||
range = model.getFullModelRange();
|
||||
|
||||
} else if (rangeOrRangeType === FormatRangeType.Selection) {
|
||||
// selection or line (when empty)
|
||||
range = editor.getSelection();
|
||||
if (range.isEmpty()) {
|
||||
range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber));
|
||||
}
|
||||
} else {
|
||||
// as is
|
||||
range = rangeOrRangeType;
|
||||
}
|
||||
return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits);
|
||||
}).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
allEdits = allEdits.concat(edits);
|
||||
}
|
||||
});
|
||||
};
|
||||
})).then(() => {
|
||||
alertFormattingEdits(allEdits);
|
||||
editor.pushUndoStop();
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
return getDocumentRangeFormattingEdits(telemetryService, workerService, model, range, options, FormatMode.Manual, token).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
alertFormattingEdits(edits);
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise<void> {
|
||||
const provider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel());
|
||||
if (provider.length === 0) {
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Full, options, token);
|
||||
}
|
||||
function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise<void> {
|
||||
|
||||
// Know how often multiple providers clash and (for now)
|
||||
// continue picking the 'first' provider
|
||||
if (provider.length !== 1) {
|
||||
/* __GDPR__
|
||||
"manyformatters" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
telemetryService.publicLog('manyformatters', {
|
||||
type: 'document',
|
||||
language: editor.getModel().getLanguageIdentifier().language,
|
||||
count: provider.length,
|
||||
});
|
||||
provider.length = 1;
|
||||
}
|
||||
const allEdits: ISingleEditOperation[] = [];
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
|
||||
let allEdits: ISingleEditOperation[] = [];
|
||||
return getDocumentFormattingEdits(telemetryService, workerService, editor.getModel(), options, FormatMode.Manual, token).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
|
||||
editor.pushUndoStop();
|
||||
return sequence(provider.map(provider => {
|
||||
// create a formatting task per provider. they run sequentially,
|
||||
// potentially undoing the working of a previous formatter
|
||||
return () => {
|
||||
const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
const model = editor.getModel();
|
||||
return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).then(edits => {
|
||||
// break edits into smaller edits
|
||||
return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits);
|
||||
}).then(edits => {
|
||||
// make edit only when the editor didn't change while
|
||||
// computing and only when there are edits
|
||||
if (state.validate(editor) && isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(editor, edits);
|
||||
allEdits = allEdits.concat(edits);
|
||||
}
|
||||
});
|
||||
};
|
||||
})).then(() => {
|
||||
alertFormattingEdits(allEdits);
|
||||
editor.pushUndoStop();
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
alertFormattingEdits(allEdits);
|
||||
editor.pushUndoStop();
|
||||
editor.focus();
|
||||
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -189,39 +124,38 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.autoFormat';
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private workerService: IEditorWorkerService;
|
||||
private callOnDispose: IDisposable[];
|
||||
private callOnModel: IDisposable[];
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _callOnDispose: IDisposable[] = [];
|
||||
private _callOnModel: IDisposable[] = [];
|
||||
|
||||
constructor(editor: ICodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
|
||||
this.editor = editor;
|
||||
this.workerService = workerService;
|
||||
this.callOnDispose = [];
|
||||
this.callOnModel = [];
|
||||
|
||||
this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update()));
|
||||
this.callOnDispose.push(editor.onDidChangeModel(() => this.update()));
|
||||
this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update()));
|
||||
this.callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this));
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IEditorWorkerService private readonly _workerService: IEditorWorkerService
|
||||
) {
|
||||
this._editor = editor;
|
||||
this._callOnDispose.push(editor.onDidChangeConfiguration(() => this.update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModel(() => this.update()));
|
||||
this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update()));
|
||||
this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this));
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
|
||||
// clean up
|
||||
this.callOnModel = dispose(this.callOnModel);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
|
||||
// we are disabled
|
||||
if (!this.editor.getConfiguration().contribInfo.formatOnType) {
|
||||
if (!this._editor.getConfiguration().contribInfo.formatOnType) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no model
|
||||
if (!this.editor.hasModel()) {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.editor.getModel();
|
||||
const model = this._editor.getModel();
|
||||
|
||||
// no support
|
||||
const [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
|
||||
@@ -234,7 +168,7 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
for (let ch of support.autoFormatTriggerCharacters) {
|
||||
triggerChars.add(ch.charCodeAt(0));
|
||||
}
|
||||
this.callOnModel.push(this.editor.onDidType((text: string) => {
|
||||
this._callOnModel.push(this._editor.onDidType((text: string) => {
|
||||
let lastCharCode = text.charCodeAt(text.length - 1);
|
||||
if (triggerChars.has(lastCharCode)) {
|
||||
this.trigger(String.fromCharCode(lastCharCode));
|
||||
@@ -243,22 +177,22 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
private trigger(ch: string): void {
|
||||
if (!this.editor.hasModel()) {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.editor.getSelections().length > 1) {
|
||||
if (this._editor.getSelections().length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.editor.getModel();
|
||||
const position = this.editor.getPosition();
|
||||
const model = this._editor.getModel();
|
||||
const position = this._editor.getPosition();
|
||||
let canceled = false;
|
||||
|
||||
// install a listener that checks if edits happens before the
|
||||
// position on which we format right now. If so, we won't
|
||||
// apply the format edits
|
||||
const unbind = this.editor.onDidChangeModelContent((e) => {
|
||||
const unbind = this._editor.onDidChangeModelContent((e) => {
|
||||
if (e.isFlush) {
|
||||
// a model.setValue() was called
|
||||
// cancel only once
|
||||
@@ -279,14 +213,14 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
|
||||
});
|
||||
|
||||
let modelOpts = model.getOptions();
|
||||
|
||||
getOnTypeFormattingEdits(model, position, ch, {
|
||||
tabSize: modelOpts.tabSize,
|
||||
insertSpaces: modelOpts.insertSpaces
|
||||
}).then(edits => {
|
||||
return this.workerService.computeMoreMinimalEdits(model.uri, edits);
|
||||
}).then(edits => {
|
||||
getOnTypeFormattingEdits(
|
||||
this._telemetryService,
|
||||
this._workerService,
|
||||
model,
|
||||
position,
|
||||
ch,
|
||||
model.getFormattingOptions()
|
||||
).then(edits => {
|
||||
|
||||
unbind.dispose();
|
||||
|
||||
@@ -295,7 +229,7 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
if (isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(this.editor, edits);
|
||||
FormattingEdit.execute(this._editor, edits);
|
||||
alertFormattingEdits(edits);
|
||||
}
|
||||
|
||||
@@ -310,8 +244,8 @@ class FormatOnType implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.callOnDispose = dispose(this.callOnDispose);
|
||||
this.callOnModel = dispose(this.callOnModel);
|
||||
this._callOnDispose = dispose(this._callOnDispose);
|
||||
this._callOnModel = dispose(this._callOnModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,8 +307,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
|
||||
}
|
||||
|
||||
const model = this.editor.getModel();
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, { tabSize, insertSpaces }, CancellationToken.None);
|
||||
formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, model.getFormattingOptions(), CancellationToken.None);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -414,15 +347,9 @@ export class FormatDocumentAction extends EditorAction {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
const { tabSize, insertSpaces } = editor.getModel().getOptions();
|
||||
return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None).catch(err => {
|
||||
if (NoProviderError.is(err)) {
|
||||
notificationService.info(nls.localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language));
|
||||
}
|
||||
});
|
||||
return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,15 +378,9 @@ export class FormatSelectionAction extends EditorAction {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
const { tabSize, insertSpaces } = editor.getModel().getOptions();
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None).catch(err => {
|
||||
if (NoProviderError.is(err)) {
|
||||
notificationService.info(nls.localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language));
|
||||
}
|
||||
});
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,48 +396,12 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => {
|
||||
if (!editor || !editor.hasModel()) {
|
||||
return undefined;
|
||||
}
|
||||
const { tabSize, insertSpaces } = editor.getModel().getOptions();
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
|
||||
if (editor.getSelection().isEmpty()) {
|
||||
return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None);
|
||||
return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
} else {
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None);
|
||||
return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CommandsRegistry.registerCommand('editor.action.formatInspect', accessor => {
|
||||
|
||||
const editor = accessor.get(ICodeEditorService).getActiveCodeEditor();
|
||||
if (!editor || !editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
console.log(`Available Formatters for: ${editor.getModel().uri.toString(true)}`);
|
||||
// range formatters
|
||||
const documentRangeProvider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel());
|
||||
console.group('Range Formatters');
|
||||
if (documentRangeProvider.length === 0) {
|
||||
console.log('none');
|
||||
} else {
|
||||
documentRangeProvider.forEach(value => console.log(value.displayName));
|
||||
}
|
||||
console.groupEnd();
|
||||
|
||||
// whole document formatters
|
||||
const documentProvider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel());
|
||||
console.group('Document Formatters');
|
||||
if (documentProvider.length === 0) {
|
||||
console.log('none');
|
||||
} else {
|
||||
documentProvider.forEach(value => console.log(value.displayName));
|
||||
}
|
||||
console.groupEnd();
|
||||
});
|
||||
|
||||
MenuRegistry.addCommand({
|
||||
id: 'editor.action.formatInspect',
|
||||
category: nls.localize('cat', "Developer"),
|
||||
title: nls.localize('title', "Print Available Formatters..."),
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ export class FormattingEdit {
|
||||
|
||||
static execute(editor: ICodeEditor, _edits: TextEdit[]) {
|
||||
editor.pushUndoStop();
|
||||
let edits = FormattingEdit._handleEolEdits(editor, _edits);
|
||||
const edits = FormattingEdit._handleEolEdits(editor, _edits);
|
||||
if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) {
|
||||
// We use replace semantics and hope that markers stay put...
|
||||
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
|
||||
|
||||
@@ -51,6 +51,9 @@ export class DefinitionAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
if (!editor.hasModel()) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const editorService = accessor.get(ICodeEditorService);
|
||||
const progressService = accessor.get(IProgressService);
|
||||
@@ -112,14 +115,14 @@ export class DefinitionAction extends EditorAction {
|
||||
return getDefinitionsAtPosition(model, position, token);
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
return info && info.word
|
||||
? nls.localize('noResultWord', "No definition found for '{0}'", info.word)
|
||||
: nls.localize('generic.noResults', "No definition found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 && nls.localize('meta.title', " – {0} definitions", model.references.length);
|
||||
return model.references.length > 1 ? nls.localize('meta.title', " – {0} definitions", model.references.length) : '';
|
||||
}
|
||||
|
||||
private async _onResult(editorService: ICodeEditorService, editor: ICodeEditor, model: ReferencesModel): Promise<void> {
|
||||
@@ -127,23 +130,26 @@ export class DefinitionAction extends EditorAction {
|
||||
const msg = model.getAriaMessage();
|
||||
alert(msg);
|
||||
|
||||
if (this._configuration.openInPeek) {
|
||||
const { gotoLocation } = editor.getConfiguration().contribInfo;
|
||||
if (this._configuration.openInPeek || (gotoLocation.many === 'peek' && model.references.length > 1)) {
|
||||
this._openInPeek(editorService, editor, model);
|
||||
} else {
|
||||
} else if (editor.hasModel()) {
|
||||
const next = model.nearestReference(editor.getModel().uri, editor.getPosition());
|
||||
const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide);
|
||||
if (targetEditor && model.references.length > 1) {
|
||||
this._openInPeek(editorService, targetEditor, model);
|
||||
} else {
|
||||
model.dispose();
|
||||
if (next) {
|
||||
const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide);
|
||||
if (targetEditor && model.references.length > 1 && gotoLocation.many === 'revealAndPeek') {
|
||||
this._openInPeek(editorService, targetEditor, model);
|
||||
} else {
|
||||
model.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise<ICodeEditor> {
|
||||
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise<ICodeEditor | null> {
|
||||
// range is the target-selection-range when we have one
|
||||
// and the the fallback is the 'full' range
|
||||
let range: IRange = undefined;
|
||||
let range: IRange | undefined = undefined;
|
||||
if (isLocationLink(reference)) {
|
||||
range = reference.targetSelectionRange;
|
||||
}
|
||||
@@ -163,7 +169,7 @@ export class DefinitionAction extends EditorAction {
|
||||
|
||||
private _openInPeek(editorService: ICodeEditorService, target: ICodeEditor, model: ReferencesModel) {
|
||||
let controller = ReferencesController.get(target);
|
||||
if (controller) {
|
||||
if (controller && target.hasModel()) {
|
||||
controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), {
|
||||
getMetaTitle: (model) => {
|
||||
return this._getMetaTitle(model);
|
||||
@@ -265,14 +271,14 @@ export class DeclarationAction extends DefinitionAction {
|
||||
return getDeclarationsAtPosition(model, position, token);
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
return info && info.word
|
||||
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
|
||||
: nls.localize('decl.generic.noResults', "No declaration found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length);
|
||||
return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,14 +301,14 @@ export class GoToDeclarationAction extends DeclarationAction {
|
||||
});
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
return info && info.word
|
||||
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
|
||||
: nls.localize('decl.generic.noResults', "No declaration found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length);
|
||||
return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,14 +335,14 @@ export class ImplementationAction extends DefinitionAction {
|
||||
return getImplementationsAtPosition(model, position, token);
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
return info && info.word
|
||||
? nls.localize('goToImplementation.noResultWord', "No implementation found for '{0}'", info.word)
|
||||
: nls.localize('goToImplementation.generic.noResults', "No implementation found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 && nls.localize('meta.implementations.title', " – {0} implementations", model.references.length);
|
||||
return model.references.length > 1 ? nls.localize('meta.implementations.title', " – {0} implementations", model.references.length) : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,14 +393,14 @@ export class TypeDefinitionAction extends DefinitionAction {
|
||||
return getTypeDefinitionsAtPosition(model, position, token);
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
return info && info.word
|
||||
? nls.localize('goToTypeDefinition.noResultWord', "No type definition found for '{0}'", info.word)
|
||||
: nls.localize('goToTypeDefinition.generic.noResults', "No type definition found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 && nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length);
|
||||
return model.references.length > 1 ? nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length) : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,17 +25,18 @@ import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionComman
|
||||
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
|
||||
import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } from 'vs/editor/common/model';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.gotodefinitionwithmouse';
|
||||
static MAX_SOURCE_PREVIEW_LINES = 8;
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private toUnhook: IDisposable[];
|
||||
private decorations: string[];
|
||||
private currentWordUnderMouse: IWordAtPosition;
|
||||
private previousPromise: CancelablePromise<LocationLink[]>;
|
||||
private currentWordUnderMouse: IWordAtPosition | null;
|
||||
private previousPromise: CancelablePromise<LocationLink[] | null> | null;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@@ -51,7 +52,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
|
||||
this.toUnhook.push(linkGesture);
|
||||
|
||||
this.toUnhook.push(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => {
|
||||
this.startFindDefinition(mouseEvent, keyboardEvent);
|
||||
this.startFindDefinition(mouseEvent, withNullAsUndefined(keyboardEvent));
|
||||
}));
|
||||
|
||||
this.toUnhook.push(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => {
|
||||
@@ -79,20 +80,20 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isEnabled(mouseEvent, withKey)) {
|
||||
if (!this.editor.hasModel() || !this.isEnabled(mouseEvent, withKey)) {
|
||||
this.currentWordUnderMouse = null;
|
||||
this.removeDecorations();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find word at mouse position
|
||||
let position = mouseEvent.target.position;
|
||||
let word = position ? this.editor.getModel().getWordAtPosition(position) : null;
|
||||
const word = mouseEvent.target.position ? this.editor.getModel().getWordAtPosition(mouseEvent.target.position) : null;
|
||||
if (!word) {
|
||||
this.currentWordUnderMouse = null;
|
||||
this.removeDecorations();
|
||||
return;
|
||||
}
|
||||
const position = mouseEvent.target.position!;
|
||||
|
||||
// Return early if word at position is still the same
|
||||
if (this.currentWordUnderMouse && this.currentWordUnderMouse.startColumn === word.startColumn && this.currentWordUnderMouse.endColumn === word.endColumn && this.currentWordUnderMouse.word === word.word) {
|
||||
@@ -158,9 +159,10 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
|
||||
wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
|
||||
}
|
||||
|
||||
const modeId = this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath);
|
||||
this.addDecoration(
|
||||
wordRange,
|
||||
new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath), previewValue)
|
||||
new MarkdownString().appendCodeblock(modeId ? modeId : '', previewValue)
|
||||
);
|
||||
ref.dispose();
|
||||
});
|
||||
@@ -274,10 +276,10 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
|
||||
}
|
||||
|
||||
private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean {
|
||||
return this.editor.getModel() &&
|
||||
return this.editor.hasModel() &&
|
||||
mouseEvent.isNoneOrSingleMouseDown &&
|
||||
(mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) &&
|
||||
(mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) &&
|
||||
(mouseEvent.hasTriggerModifier || (withKey ? withKey.keyCodeIsTriggerKey : false)) &&
|
||||
DefinitionProviderRegistry.has(this.editor.getModel());
|
||||
}
|
||||
|
||||
@@ -287,12 +289,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return getDefinitionsAtPosition(model, target.position, token);
|
||||
return getDefinitionsAtPosition(model, target.position!, token);
|
||||
}
|
||||
|
||||
private gotoDefinition(target: IMouseTarget, sideBySide: boolean): Promise<any> {
|
||||
this.editor.setPosition(target.position);
|
||||
const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: undefined, label: undefined, id: undefined, precondition: undefined });
|
||||
this.editor.setPosition(target.position!);
|
||||
const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: '', label: '', id: '', precondition: null });
|
||||
return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,12 @@ import { binarySearch } from 'vs/base/common/arrays';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
class MarkerModel {
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _markers: IMarker[];
|
||||
private _nextIdx: number;
|
||||
private _toUnbind: IDisposable[];
|
||||
@@ -120,6 +122,17 @@ class MarkerModel {
|
||||
return this.canNavigate() ? this._markers[this._nextIdx] : undefined;
|
||||
}
|
||||
|
||||
set currentMarker(marker: IMarker | undefined) {
|
||||
const idx = this._nextIdx;
|
||||
this._nextIdx = -1;
|
||||
if (marker) {
|
||||
this._nextIdx = this.indexOf(marker);
|
||||
}
|
||||
if (this._nextIdx !== idx) {
|
||||
this._onCurrentMarkerChanged.fire(marker);
|
||||
}
|
||||
}
|
||||
|
||||
public move(fwd: boolean, inCircles: boolean): boolean {
|
||||
if (!this.canNavigate()) {
|
||||
this._onCurrentMarkerChanged.fire(undefined);
|
||||
@@ -181,7 +194,7 @@ class MarkerModel {
|
||||
}
|
||||
}
|
||||
|
||||
class MarkerController implements editorCommon.IEditorContribution {
|
||||
export class MarkerController implements editorCommon.IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.markerController';
|
||||
|
||||
@@ -189,10 +202,10 @@ class MarkerController implements editorCommon.IEditorContribution {
|
||||
return editor.getContribution<MarkerController>(MarkerController.ID);
|
||||
}
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _model: MarkerModel | null;
|
||||
private _widget: MarkerNavigationWidget | null;
|
||||
private _widgetVisible: IContextKey<boolean>;
|
||||
private readonly _widgetVisible: IContextKey<boolean>;
|
||||
private _disposeOnClose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -200,7 +213,8 @@ class MarkerController implements editorCommon.IEditorContribution {
|
||||
@IMarkerService private readonly _markerService: IMarkerService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService
|
||||
) {
|
||||
this._editor = editor;
|
||||
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
|
||||
@@ -231,11 +245,19 @@ class MarkerController implements editorCommon.IEditorContribution {
|
||||
this._model = new MarkerModel(this._editor, markers);
|
||||
this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._disposeOnClose);
|
||||
|
||||
this._widget = new MarkerNavigationWidget(this._editor, this._themeService);
|
||||
const prevMarkerKeybinding = this._keybindingService.lookupKeybinding(PrevMarkerAction.ID);
|
||||
const nextMarkerKeybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID);
|
||||
const actions = [
|
||||
new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } }),
|
||||
new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } })
|
||||
];
|
||||
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService);
|
||||
this._widgetVisible.set(true);
|
||||
this._widget.onDidClose(() => this._cleanUp(), this, this._disposeOnClose);
|
||||
|
||||
this._disposeOnClose.push(this._model);
|
||||
this._disposeOnClose.push(this._widget);
|
||||
this._disposeOnClose.push(...actions);
|
||||
this._disposeOnClose.push(this._widget.onDidSelectRelatedInformation(related => {
|
||||
this._editorService.openCodeEditor({
|
||||
resource: related.resource,
|
||||
@@ -280,6 +302,11 @@ class MarkerController implements editorCommon.IEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
public show(marker: IMarker): void {
|
||||
const model = this.getOrCreateModel();
|
||||
model.currentMarker = marker;
|
||||
}
|
||||
|
||||
private _onMarkerChanged(changedResources: URI[]): void {
|
||||
let editorModel = this._editor.getModel();
|
||||
if (!editorModel) {
|
||||
@@ -311,9 +338,9 @@ class MarkerController implements editorCommon.IEditorContribution {
|
||||
|
||||
class MarkerNavigationAction extends EditorAction {
|
||||
|
||||
private _isNext: boolean;
|
||||
private readonly _isNext: boolean;
|
||||
|
||||
private _multiFile: boolean;
|
||||
private readonly _multiFile: boolean;
|
||||
|
||||
constructor(next: boolean, multiFile: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
@@ -394,24 +421,30 @@ class MarkerNavigationAction extends EditorAction {
|
||||
}
|
||||
}
|
||||
|
||||
class NextMarkerAction extends MarkerNavigationAction {
|
||||
export class NextMarkerAction extends MarkerNavigationAction {
|
||||
static ID: string = 'editor.action.marker.next';
|
||||
static LABEL: string = nls.localize('markerAction.next.label', "Go to Next Problem (Error, Warning, Info)");
|
||||
constructor() {
|
||||
super(true, false, {
|
||||
id: 'editor.action.marker.next',
|
||||
label: nls.localize('markerAction.next.label', "Go to Next Problem (Error, Warning, Info)"),
|
||||
id: NextMarkerAction.ID,
|
||||
label: NextMarkerAction.LABEL,
|
||||
alias: 'Go to Next Error or Warning',
|
||||
precondition: EditorContextKeys.writable
|
||||
precondition: EditorContextKeys.writable,
|
||||
kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PrevMarkerAction extends MarkerNavigationAction {
|
||||
static ID: string = 'editor.action.marker.prev';
|
||||
static LABEL: string = nls.localize('markerAction.previous.label', "Go to Previous Problem (Error, Warning, Info)");
|
||||
constructor() {
|
||||
super(false, false, {
|
||||
id: 'editor.action.marker.prev',
|
||||
label: nls.localize('markerAction.previous.label', "Go to Previous Problem (Error, Warning, Info)"),
|
||||
id: PrevMarkerAction.ID,
|
||||
label: PrevMarkerAction.LABEL,
|
||||
alias: 'Go to Previous Error or Warning',
|
||||
precondition: EditorContextKeys.writable
|
||||
precondition: EditorContextKeys.writable,
|
||||
kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./gotoErrorWidget';
|
||||
import 'vs/css!./media/gotoErrorWidget';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
@@ -11,11 +11,9 @@ import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/marker
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
|
||||
import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, oneOf, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
@@ -23,6 +21,12 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { getBaseLabel, getPathLabel } from 'vs/base/common/labels';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { PeekViewWidget } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/referenceSearch/referencesWidget';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
class MessageWidget {
|
||||
|
||||
@@ -36,7 +40,7 @@ class MessageWidget {
|
||||
private readonly _relatedDiagnostics = new WeakMap<HTMLElement, IRelatedInformation>();
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
|
||||
constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void, ) {
|
||||
constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) {
|
||||
this._editor = editor;
|
||||
|
||||
const domNode = document.createElement('div');
|
||||
@@ -65,7 +69,6 @@ class MessageWidget {
|
||||
horizontalScrollbarSize: 3,
|
||||
verticalScrollbarSize: 3
|
||||
});
|
||||
dom.addClass(this._scrollable.getDomNode(), 'block');
|
||||
parent.appendChild(this._scrollable.getDomNode());
|
||||
this._disposables.push(this._scrollable.onScroll(e => {
|
||||
domNode.style.left = `-${e.scrollLeft}px`;
|
||||
@@ -88,11 +91,14 @@ class MessageWidget {
|
||||
}
|
||||
|
||||
dom.clearNode(this._messageBlock);
|
||||
this._editor.applyFontInfo(this._messageBlock);
|
||||
let lastLineElement = this._messageBlock;
|
||||
for (const line of lines) {
|
||||
lastLineElement = document.createElement('div');
|
||||
lastLineElement.innerText = line;
|
||||
this._editor.applyFontInfo(lastLineElement);
|
||||
if (line === '') {
|
||||
lastLineElement.style.height = this._messageBlock.style.lineHeight;
|
||||
}
|
||||
this._messageBlock.appendChild(lastLineElement);
|
||||
}
|
||||
if (source || code) {
|
||||
@@ -114,6 +120,7 @@ class MessageWidget {
|
||||
}
|
||||
|
||||
dom.clearNode(this._relatedBlock);
|
||||
this._editor.applyFontInfo(this._relatedBlock);
|
||||
if (isNonEmptyArray(relatedInformation)) {
|
||||
const relatedInformationNode = this._relatedBlock.appendChild(document.createElement('div'));
|
||||
relatedInformationNode.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * 0.66)}px`;
|
||||
@@ -123,7 +130,7 @@ class MessageWidget {
|
||||
|
||||
let container = document.createElement('div');
|
||||
|
||||
let relatedResource = document.createElement('span');
|
||||
let relatedResource = document.createElement('a');
|
||||
dom.addClass(relatedResource, 'filename');
|
||||
relatedResource.innerHTML = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `;
|
||||
relatedResource.title = getPathLabel(related.resource, undefined);
|
||||
@@ -131,7 +138,6 @@ class MessageWidget {
|
||||
|
||||
let relatedMessage = document.createElement('span');
|
||||
relatedMessage.innerText = related.message;
|
||||
this._editor.applyFontInfo(relatedMessage);
|
||||
|
||||
container.appendChild(relatedResource);
|
||||
container.appendChild(relatedMessage);
|
||||
@@ -149,6 +155,7 @@ class MessageWidget {
|
||||
|
||||
layout(height: number, width: number): void {
|
||||
this._scrollable.getDomNode().style.height = `${height}px`;
|
||||
this._scrollable.getDomNode().style.width = `${width}px`;
|
||||
this._scrollable.setScrollDimensions({ width, height });
|
||||
}
|
||||
|
||||
@@ -157,22 +164,23 @@ class MessageWidget {
|
||||
}
|
||||
}
|
||||
|
||||
export class MarkerNavigationWidget extends ZoneWidget {
|
||||
export class MarkerNavigationWidget extends PeekViewWidget {
|
||||
|
||||
private _parentContainer: HTMLElement;
|
||||
private _container: HTMLElement;
|
||||
private _title: HTMLElement;
|
||||
private _message: MessageWidget;
|
||||
private _callOnDispose: IDisposable[] = [];
|
||||
private _severity: MarkerSeverity;
|
||||
private _backgroundColor: Color | null;
|
||||
private _backgroundColor?: Color;
|
||||
private _onDidSelectRelatedInformation = new Emitter<IRelatedInformation>();
|
||||
private _heightInPixel: number;
|
||||
|
||||
readonly onDidSelectRelatedInformation: Event<IRelatedInformation> = this._onDidSelectRelatedInformation.event;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
private _themeService: IThemeService
|
||||
private readonly actions: IAction[],
|
||||
private readonly _themeService: IThemeService
|
||||
) {
|
||||
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
|
||||
this._severity = MarkerSeverity.Warning;
|
||||
@@ -195,7 +203,10 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
const frameColor = theme.getColor(colorId);
|
||||
this.style({
|
||||
arrowColor: frameColor,
|
||||
frameColor: frameColor
|
||||
frameColor: frameColor,
|
||||
headerBackgroundColor: this._backgroundColor,
|
||||
primaryHeadingColor: theme.getColor(peekViewTitleForeground),
|
||||
secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground)
|
||||
}); // style() will trigger _applyStyles
|
||||
}
|
||||
|
||||
@@ -215,7 +226,18 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
this._parentContainer.focus();
|
||||
}
|
||||
|
||||
protected _fillContainer(container: HTMLElement): void {
|
||||
protected _fillHead(container: HTMLElement): void {
|
||||
super._fillHead(container);
|
||||
this._actionbarWidget.push(this.actions, { label: false, icon: true });
|
||||
}
|
||||
|
||||
protected _getActionBarOptions(): IActionBarOptions {
|
||||
return {
|
||||
orientation: ActionsOrientation.HORIZONTAL_REVERSE
|
||||
};
|
||||
}
|
||||
|
||||
protected _fillBody(container: HTMLElement): void {
|
||||
this._parentContainer = container;
|
||||
dom.addClass(container, 'marker-widget');
|
||||
this._parentContainer.tabIndex = 0;
|
||||
@@ -224,10 +246,6 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
this._container = document.createElement('div');
|
||||
container.appendChild(this._container);
|
||||
|
||||
this._title = document.createElement('div');
|
||||
this._title.className = 'block title';
|
||||
this._container.appendChild(this._title);
|
||||
|
||||
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related));
|
||||
this._disposables.push(this._message);
|
||||
}
|
||||
@@ -241,7 +259,6 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
// * title
|
||||
// * message
|
||||
this._container.classList.remove('stale');
|
||||
this._title.innerHTML = nls.localize('title.wo_source', "({0}/{1})", markerIdx, markerCount);
|
||||
this._message.update(marker);
|
||||
|
||||
// update frame color (only applied on 'show')
|
||||
@@ -254,6 +271,21 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
let position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition();
|
||||
super.show(position, this.computeRequiredHeight());
|
||||
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
const detail = markerCount > 1
|
||||
? nls.localize('problems', "{0} of {1} problems", markerIdx, markerCount)
|
||||
: nls.localize('change', "{0} of {1} problem", markerIdx, markerCount);
|
||||
this.setTitle(basename(model.uri), detail);
|
||||
}
|
||||
let headingIconClassName = 'error';
|
||||
if (this._severity === MarkerSeverity.Warning) {
|
||||
headingIconClassName = 'warning';
|
||||
} else if (this._severity === MarkerSeverity.Info) {
|
||||
headingIconClassName = 'info';
|
||||
}
|
||||
this.setTitleIcon(headingIconClassName);
|
||||
|
||||
this.editor.revealPositionInCenter(position, ScrollType.Smooth);
|
||||
|
||||
if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) {
|
||||
@@ -271,17 +303,23 @@ export class MarkerNavigationWidget extends ZoneWidget {
|
||||
this._relayout();
|
||||
}
|
||||
|
||||
protected _doLayout(heightInPixel: number, widthInPixel: number): void {
|
||||
protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void {
|
||||
super._doLayoutBody(heightInPixel, widthInPixel);
|
||||
this._heightInPixel = heightInPixel;
|
||||
this._message.layout(heightInPixel, widthInPixel);
|
||||
this._container.style.height = `${heightInPixel}px`;
|
||||
}
|
||||
|
||||
public _onWidth(widthInPixel: number): void {
|
||||
this._message.layout(this._heightInPixel, widthInPixel);
|
||||
}
|
||||
|
||||
protected _relayout(): void {
|
||||
super._relayout(this.computeRequiredHeight());
|
||||
}
|
||||
|
||||
private computeRequiredHeight() {
|
||||
return 1 + this._message.getHeightInLines();
|
||||
return 3 + this._message.getHeightInLines();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +333,10 @@ export const editorMarkerNavigationError = registerColor('editorMarkerNavigation
|
||||
export const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hc: warningDefault }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.'));
|
||||
export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hc: infoDefault }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.'));
|
||||
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const link = theme.getColor(textLinkForeground);
|
||||
if (link) {
|
||||
collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,8 +5,31 @@
|
||||
|
||||
/* marker zone */
|
||||
|
||||
.monaco-editor .peekview-widget .head .peekview-title .icon.warning {
|
||||
background: url('status-warning.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .head .peekview-title .icon.error {
|
||||
background: url('status-error.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .head .peekview-title .icon.info {
|
||||
background: url('status-info.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.warning {
|
||||
background: url('status-warning-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.error {
|
||||
background: url('status-error-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.info {
|
||||
background: url('status-info-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget {
|
||||
padding: 3px 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -16,21 +39,17 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget div.block {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .title {
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .descriptioncontainer {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
padding: 8px 12px 0px 20px;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message {
|
||||
@@ -43,8 +62,7 @@
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message .source,
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message .code,
|
||||
.monaco-editor .marker-widget .descriptioncontainer .filename {
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message .code {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16"><circle cx="8" cy="8" r="6" fill="#1E1E1E"/><path d="M8 3C5.238 3 3 5.238 3 8s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm3 7l-1 1-2-2-2 2-1-1 2-2.027L5 6l1-1 2 2 2-2 1 1-2 1.973L11 10z" fill="#F48771"/><path fill="#252526" d="M11 6l-1-1-2 2-2-2-1 1 2 1.973L5 10l1 1 2-2 2 2 1-1-2-2.027z"/></svg>
|
||||
|
After Width: | Height: | Size: 372 B |
1
src/vs/editor/contrib/gotoError/media/status-error.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle cx="8" cy="8" r="6" fill="#F6F6F6"/><path d="M8 3C5.238 3 3 5.238 3 8s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm3 7l-1 1-2-2-2 2-1-1 2-2.027L5 6l1-1 2 2 2-2 1 1-2 1.973L11 10z" fill="#E51400"/><path fill="#fff" d="M11 6l-1-1-2 2-2-2-1 1 2 1.973L5 10l1 1 2-2 2 2 1-1-2-2.027z"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle cx="8.5" cy="7.5" r="5.5" fill="#1E1E1E"/><path d="M8.5 3C6.015 3 4 5.015 4 7.5S6.015 12 8.5 12 13 9.985 13 7.5 10.985 3 8.5 3zm.5 8H8V6h1v5zm0-6H8V4h1v1z" fill="#1BA1E2"/><path d="M8 6h1v5H8V6zm0-2v1h1V4H8z" fill="#252526"/></svg>
|
||||
|
After Width: | Height: | Size: 356 B |
1
src/vs/editor/contrib/gotoError/media/status-info.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle cx="8.5" cy="7.5" r="5.5" fill="#F6F6F6"/><path d="M8.5 3C6.015 3 4 5.015 4 7.5S6.015 12 8.5 12 13 9.985 13 7.5 10.985 3 8.5 3zm.5 8H8V6h1v5zm0-6H8V4h1v1z" fill="#1BA1E2"/><path d="M8 6h1v5H8V6zm0-2v1h1V4H8z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 353 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16"><path fill="#1E1E1E" d="M7.5 2L2 12l2 2h9l2-2L9.5 2z"/><path d="M9 3H8l-4.5 9 1 1h8l1-1L9 3zm0 9H8v-1h1v1zm0-2H8V6h1v4z" fill="#fc0"/><path d="M9 10H8V6h1v4zm0 1H8v1h1v-1z"/></svg>
|
||||
|
After Width: | Height: | Size: 263 B |
1
src/vs/editor/contrib/gotoError/media/status-warning.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#F6F6F6" d="M7.5 2L2 12l2 2h9l2-2L9.5 2z"/><path d="M9 3H8l-4.5 9 1 1h8l1-1L9 3zm0 9H8v-1h1v1zm0-2H8V6h1v4z" fill="#fc0"/><path d="M9 10H8V6h1v4zm0 1H8v1h1v-1z"/></svg>
|
||||
|
After Width: | Height: | Size: 297 B |
@@ -23,12 +23,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .monaco-editor-hover-content {
|
||||
max-width: 500px;
|
||||
.monaco-editor-hover .hover-contents {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .hover-row {
|
||||
padding: 4px 5px;
|
||||
.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.monaco-editor-hover p,
|
||||
@@ -75,3 +75,21 @@
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .hover-row.status-bar {
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .hover-row.status-bar .actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .hover-row.status-bar .actions .action-container {
|
||||
margin: 0px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-editor-hover .hover-row.status-bar .actions .action-container .action .icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
@@ -19,19 +19,23 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation';
|
||||
import { ModesContentHoverWidget } from 'vs/editor/contrib/hover/modesContentHover';
|
||||
import { ModesGlyphHoverWidget } from 'vs/editor/contrib/hover/modesGlyphHover';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground, editorHoverStatusBarBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
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';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
export class ModesHoverController implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.hover';
|
||||
|
||||
private _toUnhook: IDisposable[];
|
||||
private _didChangeConfigurationHandler: IDisposable;
|
||||
private readonly _didChangeConfigurationHandler: IDisposable;
|
||||
|
||||
private _contentWidget: ModesContentHoverWidget;
|
||||
private _glyphWidget: ModesGlyphHoverWidget;
|
||||
@@ -63,6 +67,10 @@ export class ModesHoverController implements IEditorContribution {
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
@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 = [];
|
||||
@@ -205,9 +213,8 @@ 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._openerService);
|
||||
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, renderer);
|
||||
this._contentWidget = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._contextMenuService, this._bulkEditService, this._commandService, this._modeService, this._openerService);
|
||||
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
|
||||
}
|
||||
|
||||
public showContentHover(range: Range, mode: HoverStartMode, focus: boolean): void {
|
||||
@@ -263,7 +270,8 @@ class ShowHoverAction extends EditorAction {
|
||||
}
|
||||
const position = editor.getPosition();
|
||||
const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
||||
controller.showContentHover(range, HoverStartMode.Immediate, true);
|
||||
const focus = editor.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled;
|
||||
controller.showContentHover(range, HoverStartMode.Immediate, focus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +299,10 @@ registerThemingParticipant((theme, collector) => {
|
||||
if (link) {
|
||||
collector.addRule(`.monaco-editor .monaco-editor-hover a { color: ${link}; }`);
|
||||
}
|
||||
const actionsBackground = theme.getColor(editorHoverStatusBarBackground);
|
||||
if (actionsBackground) {
|
||||
collector.addRule(`.monaco-editor .monaco-editor-hover .hover-row .actions { background-color: ${actionsBackground}; }`);
|
||||
}
|
||||
const codeBackground = theme.getColor(textCodeBlockBackground);
|
||||
if (codeBackground) {
|
||||
collector.addRule(`.monaco-editor .monaco-editor-hover code { background-color: ${codeBackground}; }`);
|
||||
|
||||
@@ -47,19 +47,19 @@ export const enum HoverStartMode {
|
||||
|
||||
export class HoverOperation<Result> {
|
||||
|
||||
private _computer: IHoverComputer<Result>;
|
||||
private readonly _computer: IHoverComputer<Result>;
|
||||
private _state: ComputeHoverOperationState;
|
||||
private _hoverTime: number;
|
||||
|
||||
private _firstWaitScheduler: RunOnceScheduler;
|
||||
private _secondWaitScheduler: RunOnceScheduler;
|
||||
private _loadingMessageScheduler: RunOnceScheduler;
|
||||
private readonly _firstWaitScheduler: RunOnceScheduler;
|
||||
private readonly _secondWaitScheduler: RunOnceScheduler;
|
||||
private readonly _loadingMessageScheduler: RunOnceScheduler;
|
||||
private _asyncComputationPromise: CancelablePromise<Result> | null;
|
||||
private _asyncComputationPromiseDone: boolean;
|
||||
|
||||
private _completeCallback: (r: Result) => void;
|
||||
private _errorCallback: ((err: any) => void) | null | undefined;
|
||||
private _progressCallback: (progress: any) => void;
|
||||
private readonly _completeCallback: (r: Result) => void;
|
||||
private readonly _errorCallback: ((err: any) => void) | null | undefined;
|
||||
private readonly _progressCallback: (progress: any) => void;
|
||||
|
||||
constructor(computer: IHoverComputer<Result>, success: (r: Result) => void, error: ((err: any) => void) | null | undefined, progress: (progress: any) => void, hoverTime: number) {
|
||||
this._computer = computer;
|
||||
|
||||
@@ -16,15 +16,15 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
export class ContentHoverWidget extends Widget implements editorBrowser.IContentWidget {
|
||||
|
||||
private _id: string;
|
||||
private readonly _id: string;
|
||||
protected _editor: editorBrowser.ICodeEditor;
|
||||
private _isVisible: boolean;
|
||||
private _containerDomNode: HTMLElement;
|
||||
private _domNode: HTMLElement;
|
||||
private readonly _containerDomNode: HTMLElement;
|
||||
private readonly _domNode: HTMLElement;
|
||||
protected _showAtPosition: Position | null;
|
||||
protected _showAtRange: Range | null;
|
||||
private _stoleFocus: boolean;
|
||||
private scrollbar: DomScrollableElement;
|
||||
private readonly scrollbar: DomScrollableElement;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
// Editor.IContentWidget.allowEditorOverflow
|
||||
@@ -68,9 +68,9 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
|
||||
}
|
||||
}));
|
||||
|
||||
this._editor.onDidLayoutChange(e => this.updateMaxHeight());
|
||||
this._editor.onDidLayoutChange(e => this.layout());
|
||||
|
||||
this.updateMaxHeight();
|
||||
this.layout();
|
||||
this._editor.addContentWidget(this);
|
||||
this._showAtPosition = null;
|
||||
this._showAtRange = null;
|
||||
@@ -151,22 +151,23 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
|
||||
this.scrollbar.scanDomNode();
|
||||
}
|
||||
|
||||
private updateMaxHeight(): void {
|
||||
private layout(): void {
|
||||
const height = Math.max(this._editor.getLayoutInfo().height / 4, 250);
|
||||
const { fontSize, lineHeight } = this._editor.getConfiguration().fontInfo;
|
||||
|
||||
this._domNode.style.fontSize = `${fontSize}px`;
|
||||
this._domNode.style.lineHeight = `${lineHeight}px`;
|
||||
this._domNode.style.maxHeight = `${height}px`;
|
||||
this._domNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`;
|
||||
}
|
||||
}
|
||||
|
||||
export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWidget {
|
||||
|
||||
private _id: string;
|
||||
private readonly _id: string;
|
||||
protected _editor: editorBrowser.ICodeEditor;
|
||||
private _isVisible: boolean;
|
||||
private _domNode: HTMLElement;
|
||||
private readonly _domNode: HTMLElement;
|
||||
protected _showAtLineNumber: number;
|
||||
|
||||
constructor(id: string, editor: editorBrowser.ICodeEditor) {
|
||||
|
||||
@@ -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';
|
||||
@@ -24,11 +24,23 @@ import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
|
||||
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';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -53,13 +65,13 @@ type HoverPart = MarkdownHover | ColorHover | MarkerHover;
|
||||
|
||||
class ModesContentComputer implements IHoverComputer<HoverPart[]> {
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _result: HoverPart[];
|
||||
private _range: Range | null;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
private _markerDecorationsService: IMarkerDecorationsService
|
||||
private readonly _markerDecorationsService: IMarkerDecorationsService
|
||||
) {
|
||||
this._editor = editor;
|
||||
this._range = null;
|
||||
@@ -179,7 +191,7 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
|
||||
|
||||
private _getLoadingMessage(): HoverPart {
|
||||
return {
|
||||
range: this._range || undefined,
|
||||
range: withNullAsUndefined(this._range),
|
||||
contents: [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]
|
||||
};
|
||||
}
|
||||
@@ -191,11 +203,10 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
|
||||
private _messages: HoverPart[];
|
||||
private _lastRange: Range | null;
|
||||
private _computer: ModesContentComputer;
|
||||
private _hoverOperation: HoverOperation<HoverPart[]>;
|
||||
private readonly _computer: ModesContentComputer;
|
||||
private readonly _hoverOperation: HoverOperation<HoverPart[]>;
|
||||
private _highlightDecorations: string[];
|
||||
private _isChangingDecorations: boolean;
|
||||
private _markdownRenderer: MarkdownRenderer;
|
||||
private _shouldFocus: boolean;
|
||||
private _colorPicker: ColorPickerWidget | null;
|
||||
|
||||
@@ -203,9 +214,13 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
markdownRenderer: MarkdownRenderer,
|
||||
markerDecorationsService: IMarkerDecorationsService,
|
||||
private readonly _themeService: IThemeService,
|
||||
private readonly _keybindingService: IKeybindingService,
|
||||
private readonly _contextMenuService: IContextMenuService,
|
||||
private readonly _bulkEditService: IBulkEditService,
|
||||
private readonly _commandService: ICommandService,
|
||||
private readonly _modeService: IModeService,
|
||||
private readonly _openerService: IOpenerService | null = NullOpenerService,
|
||||
) {
|
||||
super(ModesContentHoverWidget.ID, editor);
|
||||
@@ -216,9 +231,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
this._highlightDecorations = [];
|
||||
this._isChangingDecorations = false;
|
||||
|
||||
this._markdownRenderer = markdownRenderer;
|
||||
this._register(markdownRenderer.onDidRenderCodeBlock(this.onContentsChange, this));
|
||||
|
||||
this._hoverOperation = new HoverOperation(
|
||||
this._computer,
|
||||
result => this._withResult(result, true),
|
||||
@@ -343,7 +355,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
let isEmptyHoverContent = true;
|
||||
|
||||
let containColorPicker = false;
|
||||
let markdownDisposeable: IDisposable;
|
||||
let markdownDisposeables: IDisposable[] = [];
|
||||
const markerMessages: MarkerHover[] = [];
|
||||
messages.forEach((msg) => {
|
||||
if (!msg.range) {
|
||||
return;
|
||||
@@ -433,25 +446,39 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
this.updateContents(fragment);
|
||||
this._colorPicker.layout();
|
||||
|
||||
this.renderDisposable = combinedDisposable([colorListener, colorChangeListener, widget, markdownDisposeable]);
|
||||
this.renderDisposable = combinedDisposable([colorListener, colorChangeListener, widget, ...markdownDisposeables]);
|
||||
});
|
||||
} else {
|
||||
if (msg instanceof MarkerHover) {
|
||||
markerMessages.push(msg);
|
||||
isEmptyHoverContent = false;
|
||||
fragment.appendChild($('div.hover-row', undefined, this.renderMarkerHover(msg)));
|
||||
} else {
|
||||
msg.contents
|
||||
.filter(contents => !isEmptyMarkdownString(contents))
|
||||
.forEach(contents => {
|
||||
const renderedContents = this._markdownRenderer.render(contents);
|
||||
markdownDisposeable = renderedContents;
|
||||
fragment.appendChild($('div.hover-row', undefined, renderedContents.element));
|
||||
const markdownHoverElement = $('div.hover-row.markdown-hover');
|
||||
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
|
||||
const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService);
|
||||
markdownDisposeables.push(renderer.onDidRenderCodeBlock(() => {
|
||||
hoverContentsElement.className = 'hover-contents code-hover-contents';
|
||||
this.onContentsChange();
|
||||
}));
|
||||
const renderedContents = renderer.render(contents);
|
||||
hoverContentsElement.appendChild(renderedContents.element);
|
||||
fragment.appendChild(markdownHoverElement);
|
||||
markdownDisposeables.push(renderedContents);
|
||||
isEmptyHoverContent = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (markerMessages.length) {
|
||||
markerMessages.forEach(msg => fragment.appendChild(this.renderMarkerHover(msg)));
|
||||
const markerHoverForStatusbar = markerMessages.length === 1 ? markerMessages[0] : markerMessages.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0];
|
||||
fragment.appendChild(this.renderMarkerStatusbar(markerHoverForStatusbar));
|
||||
}
|
||||
|
||||
// show
|
||||
|
||||
if (!containColorPicker && !isEmptyHoverContent) {
|
||||
@@ -468,27 +495,28 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
}
|
||||
|
||||
private renderMarkerHover(markerHover: MarkerHover): HTMLElement {
|
||||
const hoverElement = $('div');
|
||||
const hoverElement = $('div.hover-row');
|
||||
const markerElement = dom.append(hoverElement, $('div.marker.hover-contents'));
|
||||
const { source, message, code, relatedInformation } = markerHover.marker;
|
||||
|
||||
const messageElement = dom.append(hoverElement, $('span'));
|
||||
this._editor.applyFontInfo(markerElement);
|
||||
const messageElement = dom.append(markerElement, $('span'));
|
||||
messageElement.style.whiteSpace = 'pre-wrap';
|
||||
messageElement.innerText = message.trim();
|
||||
this._editor.applyFontInfo(messageElement);
|
||||
messageElement.innerText = message;
|
||||
|
||||
if (source || code) {
|
||||
const detailsElement = dom.append(hoverElement, $('span'));
|
||||
const detailsElement = dom.append(markerElement, $('span'));
|
||||
detailsElement.style.opacity = '0.6';
|
||||
detailsElement.style.paddingLeft = '6px';
|
||||
detailsElement.innerText = source && code ? `${source}(${code})` : `(${code})`;
|
||||
detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
|
||||
}
|
||||
|
||||
if (isNonEmptyArray(relatedInformation)) {
|
||||
const listElement = dom.append(hoverElement, $('ul'));
|
||||
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
|
||||
const item = dom.append(listElement, $('li'));
|
||||
const a = dom.append(item, $('a'));
|
||||
a.innerText = `${basename(resource.path)}(${startLineNumber}, ${startColumn})`;
|
||||
const relatedInfoContainer = dom.append(markerElement, $('div'));
|
||||
relatedInfoContainer.style.marginTop = '8px';
|
||||
const a = dom.append(relatedInfoContainer, $('a'));
|
||||
a.innerText = `${basename(resource)}(${startLineNumber}, ${startColumn}): `;
|
||||
a.style.cursor = 'pointer';
|
||||
a.onclick = e => {
|
||||
e.stopPropagation();
|
||||
@@ -497,13 +525,84 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
||||
this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` })).catch(onUnexpectedError);
|
||||
}
|
||||
};
|
||||
const messageElement = dom.append<HTMLAnchorElement>(item, $('span'));
|
||||
messageElement.innerText = `: ${message}`;
|
||||
const messageElement = dom.append<HTMLAnchorElement>(relatedInfoContainer, $('span'));
|
||||
messageElement.innerText = message;
|
||||
this._editor.applyFontInfo(messageElement);
|
||||
}
|
||||
}
|
||||
|
||||
return hoverElement;
|
||||
}
|
||||
|
||||
private renderMarkerStatusbar(markerHover: MarkerHover): HTMLElement {
|
||||
const hoverElement = $('div.hover-row.status-bar');
|
||||
const disposables: IDisposable[] = [];
|
||||
const actionsElement = dom.append(hoverElement, $('div.actions'));
|
||||
disposables.push(this.renderAction(actionsElement, {
|
||||
label: nls.localize('quick fixes', "Quick Fix..."),
|
||||
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
|
||||
});
|
||||
}
|
||||
}));
|
||||
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
|
||||
disposables.push(this.renderAction(actionsElement, {
|
||||
label: nls.localize('peek problem', "Peek Problem"),
|
||||
commandId: NextMarkerAction.ID,
|
||||
run: () => {
|
||||
this.hide();
|
||||
MarkerController.get(this._editor).show(markerHover.marker);
|
||||
this._editor.focus();
|
||||
}
|
||||
}));
|
||||
}
|
||||
this.renderDisposable = combinedDisposable(disposables);
|
||||
return hoverElement;
|
||||
}
|
||||
|
||||
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.actions.length) {
|
||||
return codeActions.actions.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;
|
||||
const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId);
|
||||
if (keybinding) {
|
||||
label.title = `${actionOptions.label} (${keybinding.getLabel()})`;
|
||||
}
|
||||
return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
actionOptions.run(actionContainer);
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
|
||||
className: 'hoverHighlight'
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
|
||||
import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
export interface IHoverMessage {
|
||||
value: IMarkdownString;
|
||||
@@ -17,7 +19,7 @@ export interface IHoverMessage {
|
||||
|
||||
class MarginComputer implements IHoverComputer<IHoverMessage[]> {
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _lineNumber: number;
|
||||
private _result: IHoverMessage[];
|
||||
|
||||
@@ -89,17 +91,21 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
|
||||
private _messages: IHoverMessage[];
|
||||
private _lastLineNumber: number;
|
||||
|
||||
private _markdownRenderer: MarkdownRenderer;
|
||||
private _computer: MarginComputer;
|
||||
private _hoverOperation: HoverOperation<IHoverMessage[]>;
|
||||
private readonly _markdownRenderer: MarkdownRenderer;
|
||||
private readonly _computer: MarginComputer;
|
||||
private readonly _hoverOperation: HoverOperation<IHoverMessage[]>;
|
||||
private _renderDisposeables: IDisposable[];
|
||||
|
||||
constructor(editor: ICodeEditor, markdownRenderer: MarkdownRenderer) {
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
modeService: IModeService,
|
||||
openerService: IOpenerService | null = NullOpenerService,
|
||||
) {
|
||||
super(ModesGlyphHoverWidget.ID, editor);
|
||||
|
||||
this._lastLineNumber = -1;
|
||||
|
||||
this._markdownRenderer = markdownRenderer;
|
||||
this._markdownRenderer = new MarkdownRenderer(this._editor, modeService, openerService);
|
||||
this._computer = new MarginComputer(this._editor);
|
||||
|
||||
this._hoverOperation = new HoverOperation(
|
||||
|
||||
@@ -10,9 +10,9 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export class InPlaceReplaceCommand implements editorCommon.ICommand {
|
||||
|
||||
private _editRange: Range;
|
||||
private _originalSelection: Selection;
|
||||
private _text: string;
|
||||
private readonly _editRange: Range;
|
||||
private readonly _originalSelection: Selection;
|
||||
private readonly _text: string;
|
||||
|
||||
constructor(editRange: Range, originalSelection: Selection, text: string) {
|
||||
this._editRange = editRange;
|
||||
|
||||
@@ -23,28 +23,6 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
|
||||
count = count || 1;
|
||||
let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
}
|
||||
|
||||
export function unshiftIndent(tabSize: number, indentation: string, count?: number): string {
|
||||
count = count || 1;
|
||||
let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
}
|
||||
|
||||
export function getReindentEditOperations(model: ITextModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] {
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
@@ -76,7 +54,15 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
|
||||
return [];
|
||||
}
|
||||
|
||||
let { tabSize, insertSpaces } = model.getOptions();
|
||||
const { tabSize, indentSize, insertSpaces } = model.getOptions();
|
||||
const shiftIndent = (indentation: string, count?: number) => {
|
||||
count = count || 1;
|
||||
return ShiftCommand.shiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
|
||||
};
|
||||
const unshiftIndent = (indentation: string, count?: number) => {
|
||||
count = count || 1;
|
||||
return ShiftCommand.unshiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
|
||||
};
|
||||
let indentEdits: IIdentifiedSingleEditOperation[] = [];
|
||||
|
||||
// indentation being passed to lines below
|
||||
@@ -92,12 +78,12 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
|
||||
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
globalIndent = unshiftIndent(tabSize, globalIndent);
|
||||
globalIndent = unshiftIndent(globalIndent);
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
|
||||
}
|
||||
if (currentLineText !== adjustedLineContent) {
|
||||
indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, tabSize, insertSpaces)));
|
||||
indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, indentSize, insertSpaces)));
|
||||
}
|
||||
} else {
|
||||
globalIndent = strings.getLeadingWhitespace(currentLineText);
|
||||
@@ -107,11 +93,11 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
|
||||
let idealIndentForNextLine: string = globalIndent;
|
||||
|
||||
if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
globalIndent = shiftIndent(tabSize, globalIndent);
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
globalIndent = shiftIndent(globalIndent);
|
||||
}
|
||||
else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
}
|
||||
|
||||
startLineNumber++;
|
||||
@@ -123,12 +109,12 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
|
||||
let adjustedLineContent = idealIndentForNextLine + text.substring(oldIndentation.length);
|
||||
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = unshiftIndent(tabSize, idealIndentForNextLine);
|
||||
globalIndent = unshiftIndent(tabSize, globalIndent);
|
||||
idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);
|
||||
globalIndent = unshiftIndent(globalIndent);
|
||||
}
|
||||
|
||||
if (oldIndentation !== idealIndentForNextLine) {
|
||||
indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, tabSize, insertSpaces)));
|
||||
indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces)));
|
||||
}
|
||||
|
||||
// calculate idealIndentForNextLine
|
||||
@@ -137,10 +123,10 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
|
||||
// but don't change globalIndent and idealIndentForNextLine.
|
||||
continue;
|
||||
} else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
globalIndent = shiftIndent(tabSize, globalIndent);
|
||||
globalIndent = shiftIndent(globalIndent);
|
||||
idealIndentForNextLine = globalIndent;
|
||||
} else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
idealIndentForNextLine = shiftIndent(tabSize, idealIndentForNextLine);
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
} else {
|
||||
idealIndentForNextLine = globalIndent;
|
||||
}
|
||||
@@ -219,7 +205,7 @@ export class IndentationToTabsAction extends EditorAction {
|
||||
|
||||
export class ChangeIndentationSizeAction extends EditorAction {
|
||||
|
||||
constructor(private insertSpaces: boolean, opts: IActionOptions) {
|
||||
constructor(private readonly insertSpaces: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
@@ -389,9 +375,9 @@ export class ReindentSelectedLinesAction extends EditorAction {
|
||||
|
||||
export class AutoIndentOnPasteCommand implements ICommand {
|
||||
|
||||
private _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[];
|
||||
private readonly _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[];
|
||||
|
||||
private _initialSelection: Selection;
|
||||
private readonly _initialSelection: Selection;
|
||||
private _selectionId: string;
|
||||
|
||||
constructor(edits: TextEdit[], initialSelection: Selection) {
|
||||
@@ -436,7 +422,7 @@ export class AutoIndentOnPasteCommand implements ICommand {
|
||||
export class AutoIndentOnPaste implements IEditorContribution {
|
||||
private static readonly ID = 'editor.contrib.autoIndentOnPaste';
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private callOnDispose: IDisposable[];
|
||||
private callOnModel: IDisposable[];
|
||||
|
||||
@@ -484,28 +470,16 @@ export class AutoIndentOnPaste implements IEditorContribution {
|
||||
if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const { tabSize, indentSize, insertSpaces } = model.getOptions();
|
||||
this.editor.pushUndoStop();
|
||||
let textEdits: TextEdit[] = [];
|
||||
|
||||
let indentConverter = {
|
||||
shiftIndent: (indentation: string) => {
|
||||
let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + 1, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
return ShiftCommand.shiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
|
||||
},
|
||||
unshiftIndent: (indentation: string) => {
|
||||
let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + 1, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
return ShiftCommand.unshiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -679,7 +653,7 @@ export class IndentationToSpacesCommand implements ICommand {
|
||||
|
||||
private selectionId: string;
|
||||
|
||||
constructor(private selection: Selection, private tabSize: number) { }
|
||||
constructor(private readonly selection: Selection, private tabSize: number) { }
|
||||
|
||||
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
|
||||
this.selectionId = builder.trackSelection(this.selection);
|
||||
@@ -695,7 +669,7 @@ export class IndentationToTabsCommand implements ICommand {
|
||||
|
||||
private selectionId: string;
|
||||
|
||||
constructor(private selection: Selection, private tabSize: number) { }
|
||||
constructor(private readonly selection: Selection, private tabSize: number) { }
|
||||
|
||||
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
|
||||
this.selectionId = builder.trackSelection(this.selection);
|
||||
|
||||
@@ -10,8 +10,8 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export class CopyLinesCommand implements editorCommon.ICommand {
|
||||
|
||||
private _selection: Selection;
|
||||
private _isCopyingDown: boolean;
|
||||
private readonly _selection: Selection;
|
||||
private readonly _isCopyingDown: boolean;
|
||||
|
||||
private _selectionDirection: SelectionDirection;
|
||||
private _selectionId: string;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export class DeleteLinesCommand implements ICommand {
|
||||
|
||||
private startLineNumber: number;
|
||||
private endLineNumber: number;
|
||||
private restoreCursorToColumn: number;
|
||||
|
||||
constructor(startLineNumber: number, endLineNumber: number, restoreCursorToColumn: number) {
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.endLineNumber = endLineNumber;
|
||||
this.restoreCursorToColumn = restoreCursorToColumn;
|
||||
}
|
||||
|
||||
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
return;
|
||||
}
|
||||
|
||||
let startLineNumber = this.startLineNumber;
|
||||
let endLineNumber = this.endLineNumber;
|
||||
|
||||
let startColumn = 1;
|
||||
let endColumn = model.getLineMaxColumn(endLineNumber);
|
||||
if (endLineNumber < model.getLineCount()) {
|
||||
endLineNumber += 1;
|
||||
endColumn = 1;
|
||||
} else if (startLineNumber > 1) {
|
||||
startLineNumber -= 1;
|
||||
startColumn = model.getLineMaxColumn(startLineNumber);
|
||||
}
|
||||
|
||||
builder.addTrackedEditOperation(new Range(startLineNumber, startColumn, endLineNumber, endColumn), null);
|
||||
}
|
||||
|
||||
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
|
||||
let inverseEditOperations = helper.getInverseEditOperations();
|
||||
let srcRange = inverseEditOperations[0].range;
|
||||
return new Selection(
|
||||
srcRange.endLineNumber,
|
||||
this.restoreCursorToColumn,
|
||||
srcRange.endLineNumber,
|
||||
this.restoreCursorToColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
|
||||
import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
|
||||
@@ -17,9 +17,8 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ICommand } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
||||
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
|
||||
import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/copyLinesCommand';
|
||||
import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/deleteLinesCommand';
|
||||
import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand';
|
||||
import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
@@ -29,7 +28,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
|
||||
|
||||
abstract class AbstractCopyLinesAction extends EditorAction {
|
||||
|
||||
private down: boolean;
|
||||
private readonly down: boolean;
|
||||
|
||||
constructor(down: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
@@ -101,7 +100,7 @@ class CopyLinesDownAction extends AbstractCopyLinesAction {
|
||||
|
||||
abstract class AbstractMoveLinesAction extends EditorAction {
|
||||
|
||||
private down: boolean;
|
||||
private readonly down: boolean;
|
||||
|
||||
constructor(down: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
@@ -171,7 +170,7 @@ class MoveLinesDownAction extends AbstractMoveLinesAction {
|
||||
}
|
||||
|
||||
export abstract class AbstractSortLinesAction extends EditorAction {
|
||||
private descending: boolean;
|
||||
private readonly descending: boolean;
|
||||
|
||||
constructor(descending: boolean, opts: IActionOptions) {
|
||||
super(opts);
|
||||
@@ -265,6 +264,7 @@ export class TrimTrailingWhitespaceAction extends EditorAction {
|
||||
|
||||
interface IDeleteLinesOperation {
|
||||
startLineNumber: number;
|
||||
selectionStartColumn: number;
|
||||
endLineNumber: number;
|
||||
positionColumn: number;
|
||||
}
|
||||
@@ -286,26 +286,50 @@ export class DeleteLinesAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ops = this._getLinesToRemove(editor);
|
||||
|
||||
// Finally, construct the delete lines commands
|
||||
let commands: ICommand[] = ops.map((op) => {
|
||||
return new DeleteLinesCommand(op.startLineNumber, op.endLineNumber, op.positionColumn);
|
||||
});
|
||||
let model: ITextModel = editor.getModel();
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
return;
|
||||
}
|
||||
|
||||
let linesDeleted = 0;
|
||||
let edits: IIdentifiedSingleEditOperation[] = [];
|
||||
let cursorState: Selection[] = [];
|
||||
for (let i = 0, len = ops.length; i < len; i++) {
|
||||
const op = ops[i];
|
||||
|
||||
let startLineNumber = op.startLineNumber;
|
||||
let endLineNumber = op.endLineNumber;
|
||||
|
||||
let startColumn = 1;
|
||||
let endColumn = model.getLineMaxColumn(endLineNumber);
|
||||
if (endLineNumber < model.getLineCount()) {
|
||||
endLineNumber += 1;
|
||||
endColumn = 1;
|
||||
} else if (startLineNumber > 1) {
|
||||
startLineNumber -= 1;
|
||||
startColumn = model.getLineMaxColumn(startLineNumber);
|
||||
}
|
||||
|
||||
edits.push(EditOperation.replace(new Selection(startLineNumber, startColumn, endLineNumber, endColumn), ''));
|
||||
cursorState.push(new Selection(startLineNumber - linesDeleted, op.positionColumn, startLineNumber - linesDeleted, op.positionColumn));
|
||||
linesDeleted += (op.endLineNumber - op.startLineNumber + 1);
|
||||
}
|
||||
|
||||
editor.pushUndoStop();
|
||||
editor.executeCommands(this.id, commands);
|
||||
editor.executeEdits(this.id, edits, cursorState);
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
|
||||
private _getLinesToRemove(editor: ICodeEditor): IDeleteLinesOperation[] {
|
||||
private _getLinesToRemove(editor: IActiveCodeEditor): IDeleteLinesOperation[] {
|
||||
// Construct delete operations
|
||||
let selections = editor.getSelections();
|
||||
if (selections === null) {
|
||||
return [];
|
||||
}
|
||||
let operations: IDeleteLinesOperation[] = selections.map((s) => {
|
||||
let operations: IDeleteLinesOperation[] = editor.getSelections().map((s) => {
|
||||
|
||||
let endLineNumber = s.endLineNumber;
|
||||
if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) {
|
||||
@@ -314,6 +338,7 @@ export class DeleteLinesAction extends EditorAction {
|
||||
|
||||
return {
|
||||
startLineNumber: s.startLineNumber,
|
||||
selectionStartColumn: s.selectionStartColumn,
|
||||
endLineNumber: endLineNumber,
|
||||
positionColumn: s.positionColumn
|
||||
};
|
||||
@@ -445,10 +470,10 @@ export class InsertLineAfterAction extends EditorAction {
|
||||
|
||||
export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction {
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
const primaryCursor = editor.getSelection();
|
||||
if (primaryCursor === null) {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const primaryCursor = editor.getSelection();
|
||||
|
||||
let rangesToDelete = this._getRangesToDelete(editor);
|
||||
// merge overlapping selections
|
||||
@@ -483,7 +508,7 @@ export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction {
|
||||
*/
|
||||
protected abstract _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[];
|
||||
|
||||
protected abstract _getRangesToDelete(editor: ICodeEditor): Range[];
|
||||
protected abstract _getRangesToDelete(editor: IActiveCodeEditor): Range[];
|
||||
}
|
||||
|
||||
export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
|
||||
@@ -532,7 +557,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
|
||||
return endCursorState;
|
||||
}
|
||||
|
||||
_getRangesToDelete(editor: ICodeEditor): Range[] {
|
||||
_getRangesToDelete(editor: IActiveCodeEditor): Range[] {
|
||||
let selections = editor.getSelections();
|
||||
if (selections === null) {
|
||||
return [];
|
||||
@@ -550,7 +575,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
|
||||
if (selection.isEmpty()) {
|
||||
if (selection.startColumn === 1) {
|
||||
let deleteFromLine = Math.max(1, selection.startLineNumber - 1);
|
||||
let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model!.getLineContent(deleteFromLine).length + 1;
|
||||
let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineContent(deleteFromLine).length + 1;
|
||||
return new Range(deleteFromLine, deleteFromColumn, selection.startLineNumber, 1);
|
||||
} else {
|
||||
return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn);
|
||||
@@ -601,7 +626,7 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
|
||||
return endCursorState;
|
||||
}
|
||||
|
||||
_getRangesToDelete(editor: ICodeEditor): Range[] {
|
||||
_getRangesToDelete(editor: IActiveCodeEditor): Range[] {
|
||||
let model = editor.getModel();
|
||||
if (model === null) {
|
||||
return [];
|
||||
@@ -615,7 +640,7 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
|
||||
|
||||
let rangesToDelete: Range[] = selections.map((sel) => {
|
||||
if (sel.isEmpty()) {
|
||||
const maxColumn = model!.getLineMaxColumn(sel.startLineNumber);
|
||||
const maxColumn = model.getLineMaxColumn(sel.startLineNumber);
|
||||
|
||||
if (sel.startColumn === maxColumn) {
|
||||
return new Range(sel.startLineNumber, sel.startColumn, sel.startLineNumber + 1, 1);
|
||||
|
||||
@@ -16,9 +16,9 @@ import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils';
|
||||
|
||||
export class MoveLinesCommand implements ICommand {
|
||||
|
||||
private _selection: Selection;
|
||||
private _isMovingDown: boolean;
|
||||
private _autoIndent: boolean;
|
||||
private readonly _selection: Selection;
|
||||
private readonly _isMovingDown: boolean;
|
||||
private readonly _autoIndent: boolean;
|
||||
|
||||
private _selectionId: string;
|
||||
private _moveEndPositionDown: boolean;
|
||||
@@ -50,9 +50,8 @@ export class MoveLinesCommand implements ICommand {
|
||||
s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
|
||||
}
|
||||
|
||||
let tabSize = model.getOptions().tabSize;
|
||||
let insertSpaces = model.getOptions().insertSpaces;
|
||||
let indentConverter = this.buildIndentConverter(tabSize);
|
||||
const { tabSize, indentSize, insertSpaces } = model.getOptions();
|
||||
let indentConverter = this.buildIndentConverter(tabSize, indentSize, insertSpaces);
|
||||
let virtualModel = {
|
||||
getLineTokens: (lineNumber: number) => {
|
||||
return model.getLineTokens(lineNumber);
|
||||
@@ -215,25 +214,13 @@ export class MoveLinesCommand implements ICommand {
|
||||
this._selectionId = builder.trackSelection(s);
|
||||
}
|
||||
|
||||
private buildIndentConverter(tabSize: number): IIndentConverter {
|
||||
private buildIndentConverter(tabSize: number, indentSize: number, insertSpaces: boolean): IIndentConverter {
|
||||
return {
|
||||
shiftIndent: (indentation) => {
|
||||
let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + 1, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
return ShiftCommand.shiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
|
||||
},
|
||||
unshiftIndent: (indentation) => {
|
||||
let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + 1, tabSize);
|
||||
let newIndentation = '';
|
||||
for (let i = 0; i < desiredIndentCount; i++) {
|
||||
newIndentation += '\t';
|
||||
}
|
||||
|
||||
return newIndentation;
|
||||
return ShiftCommand.unshiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/mod
|
||||
|
||||
export class SortLinesCommand implements editorCommon.ICommand {
|
||||
|
||||
private selection: Selection;
|
||||
private readonly selection: Selection;
|
||||
private selectionId: string;
|
||||
private descending: boolean;
|
||||
private readonly descending: boolean;
|
||||
|
||||
constructor(selection: Selection, descending: boolean) {
|
||||
this.selection = selection;
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/deleteLinesCommand';
|
||||
import { testCommand } from 'vs/editor/test/browser/testCommand';
|
||||
|
||||
function createFromSelection(selection: Selection): DeleteLinesCommand {
|
||||
let endLineNumber = selection.endLineNumber;
|
||||
if (selection.startLineNumber < selection.endLineNumber && selection.endColumn === 1) {
|
||||
endLineNumber -= 1;
|
||||
}
|
||||
return new DeleteLinesCommand(selection.startLineNumber, endLineNumber, selection.positionColumn);
|
||||
}
|
||||
|
||||
function testDeleteLinesCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
|
||||
testCommand(lines, null, selection, (sel) => createFromSelection(sel), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
suite('Editor Contrib - Delete Lines Command', () => {
|
||||
|
||||
test('empty selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3),
|
||||
[
|
||||
'first',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3)
|
||||
);
|
||||
});
|
||||
|
||||
test('empty selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('empty selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(5, 2, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line'
|
||||
],
|
||||
new Selection(4, 2, 4, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(3, 3, 2, 2),
|
||||
[
|
||||
'first',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 2, 2, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 4, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(5, 1, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line'
|
||||
],
|
||||
new Selection(4, 2, 4, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(4, 1, 2, 1),
|
||||
[
|
||||
'first',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 1, 2, 1)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 1, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(4, 1, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line'
|
||||
],
|
||||
new Selection(3, 2, 3, 2)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -899,4 +899,250 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
assert.equal(editor.getValue(), 'a\nc');
|
||||
});
|
||||
});
|
||||
|
||||
function testDeleteLinesCommand(initialText: string[], _initialSelections: Selection | Selection[], resultingText: string[], _resultingSelections: Selection | Selection[]): void {
|
||||
const initialSelections = Array.isArray(_initialSelections) ? _initialSelections : [_initialSelections];
|
||||
const resultingSelections = Array.isArray(_resultingSelections) ? _resultingSelections : [_resultingSelections];
|
||||
withTestCodeEditor(initialText, {}, (editor) => {
|
||||
editor.setSelections(initialSelections);
|
||||
const deleteLinesAction = new DeleteLinesAction();
|
||||
deleteLinesAction.run(null!, editor);
|
||||
|
||||
assert.equal(editor.getValue(), resultingText.join('\n'));
|
||||
assert.deepEqual(editor.getSelections(), resultingSelections);
|
||||
});
|
||||
}
|
||||
|
||||
test('empty selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3),
|
||||
[
|
||||
'first',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 3, 2, 3)
|
||||
);
|
||||
});
|
||||
|
||||
test('empty selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('empty selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(5, 2, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line'
|
||||
],
|
||||
new Selection(4, 2, 4, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(3, 3, 2, 2),
|
||||
[
|
||||
'first',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 2, 2, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 4, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('with selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(5, 1, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line'
|
||||
],
|
||||
new Selection(4, 2, 4, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection in middle of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(4, 1, 2, 1),
|
||||
[
|
||||
'first',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 1, 2, 1)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection at top of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(2, 1, 1, 5),
|
||||
[
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(1, 5, 1, 5)
|
||||
);
|
||||
});
|
||||
|
||||
test('with full line selection at end of lines', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line',
|
||||
'fourth line',
|
||||
'fifth'
|
||||
],
|
||||
new Selection(4, 1, 5, 2),
|
||||
[
|
||||
'first',
|
||||
'second line',
|
||||
'third line'
|
||||
],
|
||||
new Selection(3, 2, 3, 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('multicursor 1', function () {
|
||||
testDeleteLinesCommand(
|
||||
[
|
||||
'class P {',
|
||||
'',
|
||||
' getA() {',
|
||||
' if (true) {',
|
||||
' return "a";',
|
||||
' }',
|
||||
' }',
|
||||
'',
|
||||
' getB() {',
|
||||
' if (true) {',
|
||||
' return "b";',
|
||||
' }',
|
||||
' }',
|
||||
'',
|
||||
' getC() {',
|
||||
' if (true) {',
|
||||
' return "c";',
|
||||
' }',
|
||||
' }',
|
||||
'}',
|
||||
],
|
||||
[
|
||||
new Selection(4, 1, 5, 1),
|
||||
new Selection(10, 1, 11, 1),
|
||||
new Selection(16, 1, 17, 1),
|
||||
],
|
||||
[
|
||||
'class P {',
|
||||
'',
|
||||
' getA() {',
|
||||
' return "a";',
|
||||
' }',
|
||||
' }',
|
||||
'',
|
||||
' getB() {',
|
||||
' return "b";',
|
||||
' }',
|
||||
' }',
|
||||
'',
|
||||
' getC() {',
|
||||
' return "c";',
|
||||
' }',
|
||||
' }',
|
||||
'}',
|
||||
],
|
||||
[
|
||||
new Selection(4, 1, 4, 1),
|
||||
new Selection(9, 1, 9, 1),
|
||||
new Selection(14, 1, 14, 1),
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
export class Link implements ILink {
|
||||
|
||||
private _link: ILink;
|
||||
private _provider: LinkProvider;
|
||||
private readonly _provider: LinkProvider;
|
||||
|
||||
constructor(link: ILink, provider: LinkProvider) {
|
||||
this._link = link;
|
||||
@@ -33,14 +33,18 @@ export class Link implements ILink {
|
||||
return this._link.range;
|
||||
}
|
||||
|
||||
get url(): string | undefined {
|
||||
get url(): URI | string | undefined {
|
||||
return this._link.url;
|
||||
}
|
||||
|
||||
resolve(token: CancellationToken): Promise<URI> {
|
||||
if (this._link.url) {
|
||||
try {
|
||||
return Promise.resolve(URI.parse(this._link.url));
|
||||
if (typeof this._link.url === 'string') {
|
||||
return Promise.resolve(URI.parse(this._link.url));
|
||||
} else {
|
||||
return Promise.resolve(this._link.url);
|
||||
}
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('invalid'));
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class LinkOccurrence {
|
||||
}
|
||||
|
||||
private static _getOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions {
|
||||
if (link.url && /^command:/i.test(link.url)) {
|
||||
if (link.url && /^command:/i.test(link.url.toString())) {
|
||||
if (useMetaKey) {
|
||||
return (isActive ? decoration.metaCommandActive : decoration.metaCommand);
|
||||
} else {
|
||||
@@ -153,14 +153,14 @@ class LinkDetector implements editorCommon.IEditorContribution {
|
||||
|
||||
static RECOMPUTE_TIME = 1000; // ms
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private enabled: boolean;
|
||||
private listenersToRemove: IDisposable[];
|
||||
private timeout: async.TimeoutTimer;
|
||||
private readonly timeout: async.TimeoutTimer;
|
||||
private computePromise: async.CancelablePromise<Link[]> | null;
|
||||
private activeLinkDecorationId: string | null;
|
||||
private openerService: IOpenerService;
|
||||
private notificationService: INotificationService;
|
||||
private readonly openerService: IOpenerService;
|
||||
private readonly notificationService: INotificationService;
|
||||
private currentOccurrences: { [decorationId: string]: LinkOccurrence; };
|
||||
|
||||
constructor(
|
||||
@@ -341,7 +341,7 @@ class LinkDetector implements editorCommon.IEditorContribution {
|
||||
}, err => {
|
||||
// different error cases
|
||||
if (err === 'invalid') {
|
||||
this.notificationService.warn(nls.localize('invalid.url', 'Failed to open this link because it is not well-formed: {0}', link.url));
|
||||
this.notificationService.warn(nls.localize('invalid.url', 'Failed to open this link because it is not well-formed: {0}', link.url!.toString()));
|
||||
} else if (err === 'missing') {
|
||||
this.notificationService.warn(nls.localize('missing.url', 'Failed to open this link because its target is missing.'));
|
||||
} else {
|
||||
|
||||
@@ -33,8 +33,8 @@ export class MessageController extends Disposable implements editorCommon.IEdito
|
||||
return MessageController._id;
|
||||
}
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _visible: IContextKey<boolean>;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private readonly _visible: IContextKey<boolean>;
|
||||
private _messageWidget: MessageWidget;
|
||||
private _messageListeners: IDisposable[] = [];
|
||||
|
||||
@@ -125,9 +125,9 @@ class MessageWidget implements IContentWidget {
|
||||
readonly allowEditorOverflow = true;
|
||||
readonly suppressMouseDown = false;
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _position: IPosition;
|
||||
private _domNode: HTMLDivElement;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private readonly _position: IPosition;
|
||||
private readonly _domNode: HTMLDivElement;
|
||||
|
||||
static fadeOut(messageWidget: MessageWidget): IDisposable {
|
||||
let handle: any;
|
||||
|
||||
@@ -802,10 +802,10 @@ class SelectionHighlighterState {
|
||||
export class SelectionHighlighter extends Disposable implements IEditorContribution {
|
||||
private static readonly ID = 'editor.contrib.selectionHighlighter';
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private _isEnabled: boolean;
|
||||
private decorations: string[];
|
||||
private updateSoon: RunOnceScheduler;
|
||||
private readonly updateSoon: RunOnceScheduler;
|
||||
private state: SelectionHighlighterState | null;
|
||||
|
||||
constructor(editor: ICodeEditor) {
|
||||
|
||||
@@ -65,7 +65,7 @@ suite('Multicursor selection', () => {
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
getInteger: (key: string) => undefined!,
|
||||
getNumber: (key: string) => undefined!,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => undefined
|
||||
} as IStorageService);
|
||||
|
||||
@@ -26,7 +26,13 @@ namespace ParameterHintState {
|
||||
}
|
||||
|
||||
export const Default = new class { readonly type = Type.Default; };
|
||||
export const Pending = new class { readonly type = Type.Pending; };
|
||||
|
||||
export class Pending {
|
||||
readonly type = Type.Pending;
|
||||
constructor(
|
||||
readonly request: CancelablePromise<any>
|
||||
) { }
|
||||
}
|
||||
|
||||
export class Active {
|
||||
readonly type = Type.Active;
|
||||
@@ -35,7 +41,7 @@ namespace ParameterHintState {
|
||||
) { }
|
||||
}
|
||||
|
||||
export type State = typeof Default | typeof Pending | Active;
|
||||
export type State = typeof Default | Pending | Active;
|
||||
}
|
||||
|
||||
export class ParameterHintsModel extends Disposable {
|
||||
@@ -45,14 +51,13 @@ export class ParameterHintsModel extends Disposable {
|
||||
private readonly _onChangedHints = this._register(new Emitter<modes.SignatureHelp | undefined>());
|
||||
public readonly onChangedHints = this._onChangedHints.event;
|
||||
|
||||
private editor: ICodeEditor;
|
||||
private readonly editor: ICodeEditor;
|
||||
private enabled: boolean;
|
||||
private state: ParameterHintState.State = ParameterHintState.Default;
|
||||
private _state: ParameterHintState.State = ParameterHintState.Default;
|
||||
private triggerChars = new CharacterSet();
|
||||
private retriggerChars = new CharacterSet();
|
||||
|
||||
private throttledDelayer: Delayer<boolean>;
|
||||
private provideSignatureHelpRequest?: CancelablePromise<any>;
|
||||
private readonly throttledDelayer: Delayer<boolean>;
|
||||
private triggerId = 0;
|
||||
|
||||
constructor(
|
||||
@@ -78,7 +83,16 @@ export class ParameterHintsModel extends Disposable {
|
||||
this.onModelChanged();
|
||||
}
|
||||
|
||||
private get state() { return this._state; }
|
||||
private set state(value: ParameterHintState.State) {
|
||||
if (this._state.type === ParameterHintState.Type.Pending) {
|
||||
this._state.request.cancel();
|
||||
}
|
||||
this._state = value;
|
||||
}
|
||||
|
||||
cancel(silent: boolean = false): void {
|
||||
|
||||
this.state = ParameterHintState.Default;
|
||||
|
||||
this.throttledDelayer.cancel();
|
||||
@@ -86,16 +100,11 @@ export class ParameterHintsModel extends Disposable {
|
||||
if (!silent) {
|
||||
this._onChangedHints.fire(undefined);
|
||||
}
|
||||
|
||||
if (this.provideSignatureHelpRequest) {
|
||||
this.provideSignatureHelpRequest.cancel();
|
||||
this.provideSignatureHelpRequest = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
trigger(context: TriggerContext, delay?: number): void {
|
||||
const model = this.editor.getModel();
|
||||
if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) {
|
||||
if (!model || !modes.SignatureHelpProviderRegistry.has(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,12 +175,10 @@ export class ParameterHintsModel extends Disposable {
|
||||
const model = this.editor.getModel();
|
||||
const position = this.editor.getPosition();
|
||||
|
||||
this.state = ParameterHintState.Pending;
|
||||
this.state = new ParameterHintState.Pending(createCancelablePromise(token =>
|
||||
provideSignatureHelp(model, position, triggerContext, token)));
|
||||
|
||||
this.provideSignatureHelpRequest = createCancelablePromise(token =>
|
||||
provideSignatureHelp(model, position, triggerContext, token));
|
||||
|
||||
return this.provideSignatureHelpRequest.then(result => {
|
||||
return this.state.request.then(result => {
|
||||
// Check that we are still resolving the correct signature help
|
||||
if (triggerId !== this.triggerId) {
|
||||
return false;
|
||||
@@ -186,7 +193,9 @@ export class ParameterHintsModel extends Disposable {
|
||||
return true;
|
||||
}
|
||||
}).catch(error => {
|
||||
this.state = ParameterHintState.Default;
|
||||
if (triggerId === this.triggerId) {
|
||||
this.state = ParameterHintState.Default;
|
||||
}
|
||||
onUnexpectedError(error);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
|
||||
allowEditorOverflow = true;
|
||||
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
private readonly editor: ICodeEditor,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IModeService modeService: IModeService,
|
||||
@@ -72,6 +72,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
|
||||
private createParamaterHintDOMNodes() {
|
||||
this.element = $('.editor-widget.parameter-hints-widget');
|
||||
const wrapper = dom.append(this.element, $('.wrapper'));
|
||||
wrapper.tabIndex = -1;
|
||||
|
||||
const buttons = dom.append(wrapper, $('.buttons'));
|
||||
const previous = dom.append(buttons, $('.button.previous'));
|
||||
|
||||
@@ -137,7 +137,7 @@ suite('ParameterHintsModel', () => {
|
||||
assert.strictEqual(invokeCount, 2);
|
||||
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
|
||||
assert.strictEqual(context.triggerCharacter, triggerChar);
|
||||
assert.strictEqual(context.isRetrigger, false);
|
||||
assert.strictEqual(context.isRetrigger, true);
|
||||
assert.strictEqual(context.activeSignatureHelp, undefined);
|
||||
done();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M2 5.5L3.05473 4.44428L8 9.38955L12.9453 4.44428L14 5.5L8 11.5L2 5.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M2 5.5L3.05473 4.44428L8 9.38955L12.9453 4.44428L14 5.5L8 11.5L2 5.5Z" fill="#4B4B4B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 353 B |
10
src/vs/editor/contrib/referenceSearch/media/chevron-down.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M2 5.5L3.05473 4.44428L8 9.38955L12.9453 4.44428L14 5.5L8 11.5L2 5.5Z" fill="#C8C8C8"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 353 B |
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M14 10.5L12.9453 11.5557L8 6.61045L3.05473 11.5557L2 10.5L8 4.5L14 10.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M14 10.5L12.9453 11.5557L8 6.61045L3.05473 11.5557L2 10.5L8 4.5L14 10.5Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 354 B |
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M14 10.5L12.9453 11.5557L8 6.61045L3.05473 11.5557L2 10.5L8 4.5L14 10.5Z" fill="#4B4B4B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 356 B |
10
src/vs/editor/contrib/referenceSearch/media/chevron-up.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M14 10.5L12.9453 11.5557L8 6.61045L3.05473 11.5557L2 10.5L8 4.5L14 10.5Z" fill="#C8C8C8"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="12" height="12" fill="white" transform="translate(2 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 356 B |
@@ -19,6 +19,14 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .head .peekview-title .icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) {
|
||||
font-size: 0.9em;
|
||||
margin-left: 0.5em;
|
||||
@@ -73,3 +81,27 @@
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .peekview-actions .icon.chevron-up {
|
||||
background: url('chevron-up-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up {
|
||||
background: url('chevron-up.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up {
|
||||
background: url('chevron-up-inverse-hc.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .peekview-widget .peekview-actions .icon.chevron-down {
|
||||
background: url('chevron-down-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down {
|
||||
background: url('chevron-down.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down {
|
||||
background: url('chevron-down-hc.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | null {
|
||||
}
|
||||
|
||||
export interface IPeekViewStyles extends IStyles {
|
||||
headerBackgroundColor?: Color | null;
|
||||
primaryHeadingColor?: Color | null;
|
||||
secondaryHeadingColor?: Color | null;
|
||||
headerBackgroundColor?: Color;
|
||||
primaryHeadingColor?: Color;
|
||||
secondaryHeadingColor?: Color;
|
||||
}
|
||||
|
||||
export type IPeekViewOptions = IOptions & IPeekViewStyles;
|
||||
@@ -54,6 +54,7 @@ export abstract class PeekViewWidget extends ZoneWidget {
|
||||
private _onDidClose = new Emitter<PeekViewWidget>();
|
||||
|
||||
protected _headElement: HTMLDivElement;
|
||||
protected _headingIcon: HTMLElement;
|
||||
protected _primaryHeading: HTMLElement;
|
||||
protected _secondaryHeading: HTMLElement;
|
||||
protected _metaHeading: HTMLElement;
|
||||
@@ -123,10 +124,11 @@ export abstract class PeekViewWidget extends ZoneWidget {
|
||||
dom.append(this._headElement, titleElement);
|
||||
dom.addStandardDisposableListener(titleElement, 'click', event => this._onTitleClick(event));
|
||||
|
||||
this._headingIcon = dom.$('span');
|
||||
this._primaryHeading = dom.$('span.filename');
|
||||
this._secondaryHeading = dom.$('span.dirname');
|
||||
this._metaHeading = dom.$('span.meta');
|
||||
dom.append(titleElement, this._primaryHeading, this._secondaryHeading, this._metaHeading);
|
||||
dom.append(titleElement, this._headingIcon, this._primaryHeading, this._secondaryHeading, this._metaHeading);
|
||||
|
||||
const actionsContainer = dom.$('.peekview-actions');
|
||||
dom.append(this._headElement, actionsContainer);
|
||||
@@ -149,6 +151,10 @@ export abstract class PeekViewWidget extends ZoneWidget {
|
||||
// implement me
|
||||
}
|
||||
|
||||
public setTitleIcon(iconClassName: string): void {
|
||||
this._headingIcon.className = iconClassName ? `icon ${iconClassName}` : '';
|
||||
}
|
||||
|
||||
public setTitle(primaryHeading: string, secondaryHeading?: string): void {
|
||||
this._primaryHeading.innerHTML = strings.escape(primaryHeading);
|
||||
this._primaryHeading.setAttribute('aria-label', primaryHeading);
|
||||
|
||||
@@ -32,21 +32,21 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
|
||||
private static readonly ID = 'editor.contrib.referencesController';
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _widget: ReferenceWidget;
|
||||
private _model: ReferencesModel;
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _widget: ReferenceWidget | null;
|
||||
private _model: ReferencesModel | null;
|
||||
private _requestIdPool = 0;
|
||||
private _disposables: IDisposable[] = [];
|
||||
private _ignoreModelChangeEvent = false;
|
||||
|
||||
private _referenceSearchVisible: IContextKey<boolean>;
|
||||
private readonly _referenceSearchVisible: IContextKey<boolean>;
|
||||
|
||||
public static get(editor: ICodeEditor): ReferencesController {
|
||||
return editor.getContribution<ReferencesController>(ReferencesController.ID);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private _defaultTreeKeyboardSupport: boolean,
|
||||
private readonly _defaultTreeKeyboardSupport: boolean,
|
||||
editor: ICodeEditor,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@@ -66,23 +66,26 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
public dispose(): void {
|
||||
this._referenceSearchVisible.reset();
|
||||
dispose(this._disposables);
|
||||
dispose(this._widget);
|
||||
dispose(this._model);
|
||||
this._widget = null;
|
||||
this._model = null;
|
||||
this._editor = null;
|
||||
if (this._widget) {
|
||||
dispose(this._widget);
|
||||
this._widget = null;
|
||||
}
|
||||
if (this._model) {
|
||||
dispose(this._model);
|
||||
this._model = null;
|
||||
}
|
||||
}
|
||||
|
||||
public toggleWidget(range: Range, modelPromise: CancelablePromise<ReferencesModel>, options: RequestOptions): void {
|
||||
|
||||
// close current widget and return early is position didn't change
|
||||
let widgetPosition: Position;
|
||||
let widgetPosition: Position | undefined;
|
||||
if (this._widget) {
|
||||
widgetPosition = this._widget.position;
|
||||
}
|
||||
this.closeWidget();
|
||||
if (!!widgetPosition && range.containsPosition(widgetPosition)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._referenceSearchVisible.set(true);
|
||||
@@ -101,9 +104,10 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
this._widget.show(range);
|
||||
this._disposables.push(this._widget.onDidClose(() => {
|
||||
modelPromise.cancel();
|
||||
|
||||
this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL);
|
||||
this._widget = null;
|
||||
if (this._widget) {
|
||||
this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL);
|
||||
this._widget = null;
|
||||
}
|
||||
this.closeWidget();
|
||||
}));
|
||||
|
||||
@@ -119,13 +123,17 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
break;
|
||||
}
|
||||
case 'side':
|
||||
this.openReference(element, kind === 'side');
|
||||
if (element) {
|
||||
this.openReference(element, kind === 'side');
|
||||
}
|
||||
break;
|
||||
case 'goto':
|
||||
if (options.onGoto) {
|
||||
options.onGoto(element);
|
||||
} else {
|
||||
this._gotoReference(element);
|
||||
if (element) {
|
||||
if (options.onGoto) {
|
||||
options.onGoto(element);
|
||||
} else {
|
||||
this._gotoReference(element);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -148,7 +156,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
|
||||
// show widget
|
||||
return this._widget.setModel(this._model).then(() => {
|
||||
if (this._widget) { // might have been closed
|
||||
if (this._widget && this._model && this._editor.hasModel()) { // might have been closed
|
||||
// set title
|
||||
this._widget.setMetaTitle(options.getMetaTitle(this._model));
|
||||
|
||||
@@ -169,31 +177,46 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
}
|
||||
|
||||
public async goToNextOrPreviousReference(fwd: boolean) {
|
||||
if (this._model) { // can be called while still resolving...
|
||||
let source = this._model.nearestReference(this._editor.getModel().uri, this._widget.position);
|
||||
let target = this._model.nextOrPreviousReference(source, fwd);
|
||||
let editorFocus = this._editor.hasTextFocus();
|
||||
await this._widget.setSelection(target);
|
||||
await this._gotoReference(target);
|
||||
if (editorFocus) {
|
||||
this._editor.focus();
|
||||
}
|
||||
if (!this._editor.hasModel() || !this._model || !this._widget) {
|
||||
// can be called while still resolving...
|
||||
return;
|
||||
}
|
||||
const currentPosition = this._widget.position;
|
||||
if (!currentPosition) {
|
||||
return;
|
||||
}
|
||||
const source = this._model.nearestReference(this._editor.getModel().uri, currentPosition);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
const target = this._model.nextOrPreviousReference(source, fwd);
|
||||
const editorFocus = this._editor.hasTextFocus();
|
||||
await this._widget.setSelection(target);
|
||||
await this._gotoReference(target);
|
||||
if (editorFocus) {
|
||||
this._editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public closeWidget(): void {
|
||||
dispose(this._widget);
|
||||
this._widget = null;
|
||||
if (this._widget) {
|
||||
dispose(this._widget);
|
||||
this._widget = null;
|
||||
}
|
||||
this._referenceSearchVisible.reset();
|
||||
this._disposables = dispose(this._disposables);
|
||||
dispose(this._model);
|
||||
this._model = null;
|
||||
if (this._model) {
|
||||
dispose(this._model);
|
||||
this._model = null;
|
||||
}
|
||||
this._editor.focus();
|
||||
this._requestIdPool += 1; // Cancel pending requests
|
||||
}
|
||||
|
||||
private _gotoReference(ref: Location): Promise<any> {
|
||||
this._widget.hide();
|
||||
if (this._widget) {
|
||||
this._widget.hide();
|
||||
}
|
||||
|
||||
this._ignoreModelChangeEvent = true;
|
||||
const range = Range.lift(ref.range).collapseToStart();
|
||||
@@ -216,8 +239,10 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
||||
return;
|
||||
}
|
||||
|
||||
this._widget.show(range);
|
||||
this._widget.focus();
|
||||
if (this._widget) {
|
||||
this._widget.show(range);
|
||||
this._widget.focus();
|
||||
}
|
||||
|
||||
}, (err) => {
|
||||
this._ignoreModelChangeEvent = false;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -44,7 +44,7 @@ export class OneReference {
|
||||
getAriaMessage(): string {
|
||||
return localize(
|
||||
'aria.oneReference', "symbol in {0} on line {1} at column {2}",
|
||||
basename(this.uri.fsPath), this.range.startLineNumber, this.range.startColumn
|
||||
basename(this.uri), this.range.startLineNumber, this.range.startColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export class FileReferences implements IDisposable {
|
||||
private _resolved: boolean;
|
||||
private _loadFailure: any;
|
||||
|
||||
constructor(private readonly _parent: ReferencesModel, private _uri: URI) {
|
||||
constructor(private readonly _parent: ReferencesModel, private readonly _uri: URI) {
|
||||
this._children = [];
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ export class FileReferences implements IDisposable {
|
||||
getAriaMessage(): string {
|
||||
const len = this.children.length;
|
||||
if (len === 1) {
|
||||
return localize('aria.fileReferences.1', "1 symbol in {0}, full path {1}", basename(this.uri.fsPath), this.uri.fsPath);
|
||||
return localize('aria.fileReferences.1', "1 symbol in {0}, full path {1}", basename(this.uri), this.uri.fsPath);
|
||||
} else {
|
||||
return localize('aria.fileReferences.N', "{0} symbols in {1}, full path {2}", len, basename(this.uri.fsPath), this.uri.fsPath);
|
||||
return localize('aria.fileReferences.N', "{0} symbols in {1}, full path {2}", len, basename(this.uri), this.uri.fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
import { ReferencesModel, FileReferences, OneReference } from './referencesModel';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
@@ -15,7 +14,7 @@ import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { dirname, basename } from 'vs/base/common/resources';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -23,7 +22,6 @@ import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters';
|
||||
|
||||
//#region data source
|
||||
@@ -86,7 +84,7 @@ export class StringRepresentationProvider implements IKeyboardNavigationLabelPro
|
||||
getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } {
|
||||
// todo@joao `OneReference` elements are lazy and their "real" label
|
||||
// isn't known yet
|
||||
return basename(element.uri.path);
|
||||
return basename(element.uri);
|
||||
}
|
||||
|
||||
mightProducePrintableCharacter(event: IKeyboardEvent): boolean {
|
||||
@@ -126,7 +124,7 @@ class FileReferencesTemplate extends Disposable {
|
||||
|
||||
set(element: FileReferences, matches: IMatch[]) {
|
||||
let parent = dirname(element.uri);
|
||||
this.file.setLabel(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri), matches });
|
||||
this.file.setLabel(getBaseLabel(element.uri), this._uriLabel.getUriLabel(parent, { relative: true }), { title: this._uriLabel.getUriLabel(element.uri), matches });
|
||||
const len = element.children.length;
|
||||
this.badge.setCount(len);
|
||||
if (element.failure) {
|
||||
|
||||
@@ -400,7 +400,6 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
if (e.browserEvent instanceof KeyboardEvent) {
|
||||
// todo@joh make this a command
|
||||
goto = true;
|
||||
|
||||
} else if (e.browserEvent instanceof MouseEvent) {
|
||||
aside = e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey;
|
||||
goto = e.browserEvent.detail === 2;
|
||||
@@ -413,6 +412,18 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
onEvent(e.elements[0], 'show');
|
||||
}
|
||||
});
|
||||
this._tree.onDidOpen(e => {
|
||||
const aside = (e.browserEvent instanceof MouseEvent) && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey);
|
||||
const goto = !e.browserEvent || ((e.browserEvent instanceof MouseEvent) && e.browserEvent.detail === 2);
|
||||
|
||||
if (aside) {
|
||||
onEvent(e.elements[0], 'side');
|
||||
} else if (goto) {
|
||||
onEvent(e.elements[0], 'goto');
|
||||
} else {
|
||||
onEvent(e.elements[0], 'show');
|
||||
}
|
||||
});
|
||||
|
||||
dom.hide(this._treeContainer);
|
||||
}
|
||||
@@ -461,14 +472,14 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
});
|
||||
}
|
||||
|
||||
public setModel(newModel: ReferencesModel | undefined): Promise<any> | undefined {
|
||||
public setModel(newModel: ReferencesModel | undefined): Promise<any> {
|
||||
// clean up
|
||||
this._disposeOnNewModel = dispose(this._disposeOnNewModel);
|
||||
this._model = newModel;
|
||||
if (this._model) {
|
||||
return this._onNewModel();
|
||||
}
|
||||
return undefined;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private _onNewModel(): Promise<any> {
|
||||
@@ -488,7 +499,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
this._disposeOnNewModel.push(this._decorationsManager);
|
||||
|
||||
// listen on model changes
|
||||
this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.refresh(reference)));
|
||||
this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.rerender(reference)));
|
||||
|
||||
// listen on editor
|
||||
this._disposeOnNewModel.push(this._preview.onMouseDown(e => {
|
||||
@@ -543,7 +554,7 @@ export class ReferenceWidget extends PeekViewWidget {
|
||||
|
||||
// Update widget header
|
||||
if (reference.uri.scheme !== Schemas.inMemory) {
|
||||
this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri)!));
|
||||
this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri)));
|
||||
} else {
|
||||
this.setTitle(nls.localize('peekView.alternateTitle', "References"));
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ export class RenameAction extends EditorAction {
|
||||
|
||||
runCommand(accessor: ServicesAccessor, args: [URI, IPosition]): void | Promise<void> {
|
||||
const editorService = accessor.get(ICodeEditorService);
|
||||
const [uri, pos] = args || [undefined, undefined];
|
||||
const [uri, pos] = Array.isArray(args) && args || [undefined, undefined];
|
||||
|
||||
if (URI.isUri(uri) && Position.isIPosition(pos)) {
|
||||
return editorService.openCodeEditor({ resource: uri }, editorService.getActiveCodeEditor()).then(editor => {
|
||||
|
||||