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
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -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(

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
});

View File

@@ -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';

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View 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

View 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

View File

@@ -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} `;
}

View File

@@ -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);
});

View File

@@ -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(() => {

View File

@@ -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 {

View File

@@ -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
) {

View File

@@ -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()));

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) => {

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View 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'
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);
});
});

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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}; }`);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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'
}
}
}
}
}
]
}

View File

@@ -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 {

View File

@@ -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[];

View File

@@ -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() {

View File

@@ -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[]>();

View File

@@ -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 = [];

View File

@@ -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 {

View File

@@ -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 = [];

View File

@@ -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);
});

View File

@@ -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..."),
});

View File

@@ -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)));

View File

@@ -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) : '';
}
}

View File

@@ -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));
}

View File

@@ -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 }
});
}
}

View File

@@ -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}; }`);
}
});

View File

@@ -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;
}

View File

@@ -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

View 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

View 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="#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

View 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

View File

@@ -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

View 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

View File

@@ -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;
}

View File

@@ -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}; }`);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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'
});

View File

@@ -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(

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
);
}
}

View File

@@ -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);

View File

@@ -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);
}
};
}

View File

@@ -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;

View File

@@ -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)
);
});
});

View File

@@ -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),
]
);
});
});

View File

@@ -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'));
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;
});

View File

@@ -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'));

View File

@@ -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();
}

View 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="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

View 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="#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

View 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

View 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="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

View 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="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

View 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="#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

View 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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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"));
}

View File

@@ -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 => {

Some files were not shown because too many files have changed in this diff Show More