Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)

* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998

* fix pipelines

* fix strict-null-checks

* add missing files
This commit is contained in:
Anthony Dresser
2019-10-21 22:12:22 -07:00
committed by GitHub
parent 7c9be74970
commit 1e22f47304
913 changed files with 18898 additions and 16536 deletions

View File

@@ -56,16 +56,36 @@ class SelectToBracketAction extends EditorAction {
id: 'editor.action.selectToBracket',
label: nls.localize('smartSelect.selectToBracket', "Select to Bracket"),
alias: 'Select to Bracket',
precondition: undefined
precondition: undefined,
description: {
description: `Select to Bracket`,
args: [{
name: 'args',
schema: {
type: 'object',
properties: {
'selectBrackets': {
type: 'boolean',
default: true
}
},
}
}]
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = BracketMatchingController.get(editor);
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
const controller = BracketMatchingController.get(editor);
if (!controller) {
return;
}
controller.selectToBracket();
let selectBrackets = true;
if (args && args.selectBrackets === false) {
selectBrackets = false;
}
controller.selectToBracket(selectBrackets);
}
}
@@ -82,7 +102,7 @@ class BracketsData {
}
export class BracketMatchingController extends Disposable implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.bracketMatchingController';
public static readonly ID = 'editor.contrib.bracketMatchingController';
public static get(editor: ICodeEditor): BracketMatchingController {
return editor.getContribution<BracketMatchingController>(BracketMatchingController.ID);
@@ -140,10 +160,6 @@ export class BracketMatchingController extends Disposable implements editorCommo
}));
}
public getId(): string {
return BracketMatchingController.ID;
}
public jumpToBracket(): void {
if (!this._editor.hasModel()) {
return;
@@ -163,10 +179,16 @@ export class BracketMatchingController extends Disposable implements editorCommo
newCursorPosition = brackets[0].getStartPosition();
}
} else {
// find the next bracket if the position isn't on a matching bracket
const nextBracket = model.findNextBracket(position);
if (nextBracket && nextBracket.range) {
newCursorPosition = nextBracket.range.getStartPosition();
// find the enclosing brackets if the position isn't on a matching bracket
const enclosingBrackets = model.findEnclosingBrackets(position);
if (enclosingBrackets) {
newCursorPosition = enclosingBrackets[0].getStartPosition();
} else {
// no enclosing brackets, try the very first next bracket
const nextBracket = model.findNextBracket(position);
if (nextBracket && nextBracket.range) {
newCursorPosition = nextBracket.range.getStartPosition();
}
}
}
@@ -180,7 +202,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
this._editor.revealRange(newSelections[0]);
}
public selectToBracket(): void {
public selectToBracket(selectBrackets: boolean): void {
if (!this._editor.hasModel()) {
return;
}
@@ -192,36 +214,31 @@ export class BracketMatchingController extends Disposable implements editorCommo
const position = selection.getStartPosition();
let brackets = model.matchBracket(position);
let openBracket: Position | null = null;
let closeBracket: Position | null = null;
if (!brackets) {
const nextBracket = model.findNextBracket(position);
if (nextBracket && nextBracket.range) {
brackets = model.matchBracket(nextBracket.range.getStartPosition());
brackets = model.findEnclosingBrackets(position);
if (!brackets) {
const nextBracket = model.findNextBracket(position);
if (nextBracket && nextBracket.range) {
brackets = model.matchBracket(nextBracket.range.getStartPosition());
}
}
}
let selectFrom: Position | null = null;
let selectTo: Position | null = null;
if (brackets) {
if (brackets[0].startLineNumber === brackets[1].startLineNumber) {
openBracket = brackets[1].startColumn < brackets[0].startColumn ?
brackets[1].getStartPosition() : brackets[0].getStartPosition();
closeBracket = brackets[1].startColumn < brackets[0].startColumn ?
brackets[0].getEndPosition() : brackets[1].getEndPosition();
} else {
openBracket = brackets[1].startLineNumber < brackets[0].startLineNumber ?
brackets[1].getStartPosition() : brackets[0].getStartPosition();
closeBracket = brackets[1].startLineNumber < brackets[0].startLineNumber ?
brackets[0].getEndPosition() : brackets[1].getEndPosition();
}
brackets.sort(Range.compareRangesUsingStarts);
const [open, close] = brackets;
selectFrom = selectBrackets ? open.getStartPosition() : open.getEndPosition();
selectTo = selectBrackets ? close.getEndPosition() : close.getStartPosition();
}
if (openBracket && closeBracket) {
newSelections.push(new Selection(openBracket.lineNumber, openBracket.column, closeBracket.lineNumber, closeBracket.column));
if (selectFrom && selectTo) {
newSelections.push(new Selection(selectFrom.lineNumber, selectFrom.column, selectTo.lineNumber, selectTo.column));
}
});
if (newSelections.length > 0) {
this._editor.setSelections(newSelections);
this._editor.revealRange(newSelections[0]);
@@ -264,6 +281,14 @@ export class BracketMatchingController extends Disposable implements editorCommo
return;
}
const selections = this._editor.getSelections();
if (selections.length > 100) {
// no bracket matching for high numbers of selections
this._lastBracketsData = [];
this._lastVersionId = 0;
return;
}
const model = this._editor.getModel();
const versionId = model.getVersionId();
let previousData: BracketsData[] = [];
@@ -272,8 +297,6 @@ export class BracketMatchingController extends Disposable implements editorCommo
previousData = this._lastBracketsData;
}
const selections = this._editor.getSelections();
let positions: Position[] = [], positionsLen = 0;
for (let i = 0, len = selections.length; i < len; i++) {
let selection = selections[i];
@@ -302,6 +325,9 @@ export class BracketMatchingController extends Disposable implements editorCommo
newData[newDataLen++] = previousData[previousIndex];
} else {
let brackets = model.matchBracket(position);
if (!brackets) {
brackets = model.findEnclosingBrackets(position);
}
newData[newDataLen++] = new BracketsData(position, brackets);
}
}
@@ -311,7 +337,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
}
}
registerEditorContribution(BracketMatchingController);
registerEditorContribution(BracketMatchingController.ID, BracketMatchingController);
registerEditorAction(SelectToBracketAction);
registerEditorAction(JumpToBracketAction);
registerThemingParticipant((theme, collector) => {

View File

@@ -34,7 +34,7 @@ suite('bracket matching', () => {
let model = TextModel.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
// start on closing bracket
editor.setPosition(new Position(1, 20));
@@ -66,7 +66,7 @@ suite('bracket matching', () => {
let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
// start position between brackets
editor.setPosition(new Position(1, 16));
@@ -103,36 +103,36 @@ suite('bracket matching', () => {
let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
// start position in open brackets
editor.setPosition(new Position(1, 9));
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 20));
assert.deepEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
// start position in close brackets
editor.setPosition(new Position(1, 20));
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 20));
assert.deepEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
// start position between brackets
editor.setPosition(new Position(1, 16));
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 19));
assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 19));
// start position outside brackets
editor.setPosition(new Position(1, 21));
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 25));
assert.deepEqual(editor.getSelection(), new Selection(1, 23, 1, 25));
// do not break if no brackets are available
editor.setPosition(new Position(1, 26));
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 26));
assert.deepEqual(editor.getSelection(), new Selection(1, 26, 1, 26));
@@ -143,12 +143,62 @@ suite('bracket matching', () => {
mode.dispose();
});
test('issue #1772: jump to enclosing brackets', () => {
const text = [
'const x = {',
' something: [0, 1, 2],',
' another: true,',
' somethingmore: [0, 2, 4]',
'};',
].join('\n');
const mode = new BracketMode();
const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
editor.setPosition(new Position(3, 5));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getSelection(), new Selection(5, 1, 5, 1));
bracketMatchingController.dispose();
});
model.dispose();
mode.dispose();
});
test('issue #43371: argument to not select brackets', () => {
const text = [
'const x = {',
' something: [0, 1, 2],',
' another: true,',
' somethingmore: [0, 2, 4]',
'};',
].join('\n');
const mode = new BracketMode();
const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
editor.setPosition(new Position(3, 5));
bracketMatchingController.selectToBracket(false);
assert.deepEqual(editor.getSelection(), new Selection(1, 12, 5, 1));
bracketMatchingController.dispose();
});
model.dispose();
mode.dispose();
});
test('issue #45369: Select to Bracket with multicursor', () => {
let mode = new BracketMode();
let model = TextModel.createFromString('{ } { } { }', undefined, mode.getLanguageIdentifier());
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
// cursors inside brackets become selections of the entire bracket contents
editor.setSelections([
@@ -156,7 +206,7 @@ suite('bracket matching', () => {
new Selection(1, 10, 1, 10),
new Selection(1, 17, 1, 17)
]);
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),
@@ -169,7 +219,7 @@ suite('bracket matching', () => {
new Selection(1, 6, 1, 6),
new Selection(1, 14, 1, 14)
]);
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),
@@ -182,7 +232,7 @@ suite('bracket matching', () => {
new Selection(1, 13, 1, 13),
new Selection(1, 19, 1, 19)
]);
bracketMatchingController.selectToBracket();
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),

View File

@@ -5,6 +5,7 @@
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable } from 'vs/base/common/lifecycle';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -39,7 +40,7 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
export class QuickFixController extends Disposable implements IEditorContribution {
private static readonly ID = 'editor.contrib.quickFixController';
public static readonly ID = 'editor.contrib.quickFixController';
public static get(editor: ICodeEditor): QuickFixController {
return editor.getContribution<QuickFixController>(QuickFixController.ID);
@@ -47,7 +48,7 @@ export class QuickFixController extends Disposable implements IEditorContributio
private readonly _editor: ICodeEditor;
private readonly _model: CodeActionModel;
private readonly _ui: CodeActionUi;
private readonly _ui: Lazy<CodeActionUi>;
constructor(
editor: ICodeEditor,
@@ -64,31 +65,29 @@ export class QuickFixController extends Disposable implements IEditorContributio
this._editor = editor;
this._model = this._register(new CodeActionModel(this._editor, markerService, contextKeyService, progressService));
this._register(this._model.onDidChangeState((newState) => this.update(newState)));
this._register(this._model.onDidChangeState(newState => this.update(newState)));
this._ui = this._register(new CodeActionUi(editor, QuickFixAction.Id, {
applyCodeAction: async (action, retrigger) => {
try {
await this._applyCodeAction(action);
} finally {
if (retrigger) {
this._trigger({ type: 'auto', filter: {} });
this._ui = new Lazy(() =>
this._register(new CodeActionUi(editor, QuickFixAction.Id, AutoFixAction.Id, {
applyCodeAction: async (action, retrigger) => {
try {
await this._applyCodeAction(action);
} finally {
if (retrigger) {
this._trigger({ type: 'auto', filter: {} });
}
}
}
}
}, contextMenuService, keybindingService));
}, contextMenuService, keybindingService))
);
}
private update(newState: CodeActionsState.State): void {
this._ui.update(newState);
this._ui.getValue().update(newState);
}
public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) {
return this._ui.showCodeActionList(actions, at);
}
public getId(): string {
return QuickFixController.ID;
return this._ui.getValue().showCodeActionList(actions, at);
}
public manualTriggerAtCurrentPosition(

View File

@@ -7,7 +7,7 @@ import { registerEditorAction, registerEditorCommand, registerEditorContribution
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction, AutoFixAction, FixAllAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
registerEditorContribution(QuickFixController);
registerEditorContribution(QuickFixController.ID, QuickFixController);
registerEditorAction(QuickFixAction);
registerEditorAction(RefactorAction);
registerEditorAction(SourceAction);

View File

@@ -17,16 +17,18 @@ import { CodeActionWidget } from './codeActionWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { IPosition } from 'vs/editor/common/core/position';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Lazy } from 'vs/base/common/lazy';
export class CodeActionUi extends Disposable {
private readonly _codeActionWidget: CodeActionWidget;
private readonly _lightBulbWidget: LightBulbWidget;
private readonly _codeActionWidget: Lazy<CodeActionWidget>;
private readonly _lightBulbWidget: Lazy<LightBulbWidget>;
private readonly _activeCodeActions = this._register(new MutableDisposable<CodeActionSet>());
constructor(
private readonly _editor: ICodeEditor,
quickFixActionId: string,
preferredFixActionId: string,
private readonly delegate: {
applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => void
},
@@ -35,19 +37,26 @@ export class CodeActionUi extends Disposable {
) {
super();
this._codeActionWidget = this._register(new CodeActionWidget(this._editor, contextMenuService, {
onSelectCodeAction: async (action) => {
this.delegate.applyCodeAction(action, /* retrigger */ true);
}
}));
this._lightBulbWidget = this._register(new LightBulbWidget(this._editor, quickFixActionId, keybindingService));
this._codeActionWidget = new Lazy(() => {
return this._register(new CodeActionWidget(this._editor, contextMenuService, {
onSelectCodeAction: async (action) => {
this.delegate.applyCodeAction(action, /* retrigger */ true);
}
}));
});
this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this));
this._lightBulbWidget = new Lazy(() => {
const widget = this._register(new LightBulbWidget(this._editor, quickFixActionId, preferredFixActionId, keybindingService));
this._register(widget.onClick(this._handleLightBulbSelect, this));
return widget;
});
}
public async update(newState: CodeActionsState.State): Promise<void> {
if (newState.type !== CodeActionsState.Type.Triggered) {
this._lightBulbWidget.hide();
if (this._lightBulbWidget.hasValue()) {
this._lightBulbWidget.getValue().hide();
}
return;
}
@@ -59,7 +68,7 @@ export class CodeActionUi extends Disposable {
return;
}
this._lightBulbWidget.update(actions, newState.position);
this._lightBulbWidget.getValue().update(actions, newState.position);
if (!actions.actions.length && newState.trigger.context) {
MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position);
@@ -83,10 +92,10 @@ export class CodeActionUi extends Disposable {
}
}
this._activeCodeActions.value = actions;
this._codeActionWidget.show(actions, newState.position);
this._codeActionWidget.getValue().show(actions, newState.position);
} else {
// auto magically triggered
if (this._codeActionWidget.isVisible) {
if (this._codeActionWidget.getValue().isVisible) {
// TODO: Figure out if we should update the showing menu?
actions.dispose();
} else {
@@ -96,10 +105,10 @@ export class CodeActionUi extends Disposable {
}
public async showCodeActionList(actions: CodeActionSet, at?: IAnchor | IPosition): Promise<void> {
this._codeActionWidget.show(actions, at);
this._codeActionWidget.getValue().show(actions, at);
}
private _handleLightBulbSelect(e: { x: number, y: number, actions: CodeActionSet }): void {
this._codeActionWidget.show(e.actions, e);
this._codeActionWidget.getValue().show(e.actions, e);
}
}

View File

@@ -21,7 +21,7 @@ interface CodeActionWidgetDelegate {
export class CodeActionWidget extends Disposable {
private _visible: boolean;
private _visible: boolean = false;
private readonly _showingActions = this._register(new MutableDisposable<CodeActionSet>());
constructor(
@@ -30,7 +30,6 @@ export class CodeActionWidget extends Disposable {
private readonly _delegate: CodeActionWidgetDelegate,
) {
super();
this._visible = false;
}
public async show(codeActions: CodeActionSet, at?: IAnchor | IPosition): Promise<void> {

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .lightbulb-glyph,
.monaco-editor .codicon-lightbulb {
display: flex;
align-items: center;
@@ -12,6 +13,7 @@
padding-left: 2px;
}
.monaco-editor .lightbulb-glyph:hover,
.monaco-editor .codicon-lightbulb:hover {
cursor: pointer;
/* transform: scale(1.3, 1.3); */

View File

@@ -47,7 +47,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
private readonly _domNode: HTMLDivElement;
private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet }>());
private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet; }>());
public readonly onClick = this._onClick.event;
private _state: LightBulbState.State = LightBulbState.Hidden;
@@ -55,6 +55,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
constructor(
private readonly _editor: ICodeEditor,
private readonly _quickFixActionId: string,
private readonly _preferredFixActionId: string,
@IKeybindingService private readonly _keybindingService: IKeybindingService
) {
super();
@@ -66,12 +67,12 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
this._register(this._editor.onDidChangeModelContent(_ => {
// cancel when the line in question has been removed
const editorModel = this._editor.getModel();
if (this._state.type !== LightBulbState.Type.Showing || !editorModel || this._state.editorPosition.lineNumber >= editorModel.getLineCount()) {
if (this.state.type !== LightBulbState.Type.Showing || !editorModel || this.state.editorPosition.lineNumber >= editorModel.getLineCount()) {
this.hide();
}
}));
this._register(dom.addStandardDisposableListener(this._domNode, 'mousedown', e => {
if (this._state.type !== LightBulbState.Type.Showing) {
if (this.state.type !== LightBulbState.Type.Showing) {
return;
}
@@ -84,14 +85,14 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
const lineHeight = this._editor.getOption(EditorOption.lineHeight);
let pad = Math.floor(lineHeight / 3);
if (this._state.widgetPosition.position !== null && this._state.widgetPosition.position.lineNumber < this._state.editorPosition.lineNumber) {
if (this.state.widgetPosition.position !== null && this.state.widgetPosition.position.lineNumber < this.state.editorPosition.lineNumber) {
pad += lineHeight;
}
this._onClick.fire({
x: e.posx,
y: top + height + pad,
actions: this._state.actions
actions: this.state.actions
});
}));
this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => {
@@ -173,7 +174,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
}
}
this._state = new LightBulbState.Showing(actions, atPosition, {
this.state = new LightBulbState.Showing(actions, atPosition, {
position: { lineNumber: effectiveLineNumber, column: 1 },
preference: LightBulbWidget._posPref
});
@@ -181,24 +182,37 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
this._editor.layoutContentWidget(this);
}
private set title(value: string) {
this._domNode.title = value;
}
public hide(): void {
this._state = LightBulbState.Hidden;
this.state = LightBulbState.Hidden;
this._editor.layoutContentWidget(this);
}
private get state(): LightBulbState.State { return this._state; }
private set state(value) {
this._state = value;
this._updateLightBulbTitle();
}
private _updateLightBulbTitle(): void {
const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId);
let title: string;
if (kb) {
title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
} else {
title = nls.localize('quickFix', "Show Fixes");
if (this.state.type === LightBulbState.Type.Showing && this.state.actions.hasAutoFix) {
const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId);
if (preferredKb) {
this.title = nls.localize('prefferedQuickFixWithKb', "Show Fixes. Preferred Fix Available ({0})", preferredKb.getLabel());
return;
}
}
this.title = title;
const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId);
if (kb) {
this.title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
} else {
this.title = nls.localize('quickFix', "Show Fixes");
}
}
private set title(value: string) {
this._domNode.title = value;
}
}

View File

@@ -21,7 +21,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
export class CodeLensContribution implements editorCommon.IEditorContribution {
private static readonly ID: string = 'css.editor.codeLens';
public static readonly ID: string = 'css.editor.codeLens';
private _isEnabled: boolean;
@@ -78,10 +78,6 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
dispose(this._currentCodeLensModel);
}
getId(): string {
return CodeLensContribution.ID;
}
private _onModelChange(): void {
this._localDispose();
@@ -366,4 +362,4 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
}
}
registerEditorContribution(CodeLensContribution);
registerEditorContribution(CodeLensContribution.ID, CodeLensContribution);

View File

@@ -6,7 +6,7 @@
import 'vs/css!./codelensWidget';
import * as dom from 'vs/base/browser/dom';
import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays';
import { escape } from 'vs/base/common/strings';
import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
@@ -104,7 +104,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
for (let i = 0; i < symbols.length; i++) {
const command = symbols[i].command;
if (command) {
const title = escape(command.title);
const title = renderCodicons(command.title);
let part: string;
if (command.id) {
part = `<a id=${i}>${title}</a>`;

View File

@@ -25,9 +25,9 @@ const MAX_DECORATORS = 500;
export class ColorDetector extends Disposable implements IEditorContribution {
private static readonly ID: string = 'editor.contrib.colorDetector';
public static readonly ID: string = 'editor.contrib.colorDetector';
static RECOMPUTE_TIME = 1000; // ms
static readonly RECOMPUTE_TIME = 1000; // ms
private readonly _localToDispose = this._register(new DisposableStore());
private _computePromise: CancelablePromise<IColorData[]> | null;
@@ -88,10 +88,6 @@ export class ColorDetector extends Disposable implements IEditorContribution {
return this._editor.getOption(EditorOption.colorDecorators);
}
getId(): string {
return ColorDetector.ID;
}
static get(editor: ICodeEditor): ColorDetector {
return editor.getContribution<ColorDetector>(this.ID);
}
@@ -247,4 +243,4 @@ export class ColorDetector extends Disposable implements IEditorContribution {
}
}
registerEditorContribution(ColorDetector);
registerEditorContribution(ColorDetector.ID, ColorDetector);

View File

@@ -32,7 +32,7 @@ export class ColorPickerHeader extends Disposable {
this.pickedColorNode = dom.append(this.domNode, $('.picked-color'));
const colorBox = dom.append(this.domNode, $('.original-color'));
colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor);
colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || '';
this.backgroundColor = themeService.getTheme().getColor(editorHoverBackground) || Color.white;
this._register(registerThemingParticipant((theme, collector) => {
@@ -46,12 +46,12 @@ export class ColorPickerHeader extends Disposable {
}));
this._register(model.onDidChangeColor(this.onDidChangeColor, this));
this._register(model.onDidChangePresentation(this.onDidChangePresentation, this));
this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color);
this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color) || '';
dom.toggleClass(this.pickedColorNode, 'light', model.color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : model.color.isLighter());
}
private onDidChangeColor(color: Color): void {
this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color);
this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color) || '';
dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter());
this.onDidChangePresentation();
}

View File

@@ -13,6 +13,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { Constants } from 'vs/base/common/uint';
export interface IInsertionPoint {
ignore: boolean;
@@ -392,7 +393,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Adjust insertion points to have them vertically aligned in the add line comment case
*/
public static _normalizeInsertionPoint(model: ISimpleModel, lines: IInsertionPoint[], startLineNumber: number, tabSize: number): void {
let minVisibleColumn = Number.MAX_VALUE;
let minVisibleColumn = Constants.MAX_SAFE_SMALL_INTEGER;
let j: number;
let lenJ: number;

View File

@@ -26,7 +26,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
export class ContextMenuController implements IEditorContribution {
private static readonly ID = 'editor.contrib.contextmenu';
public static readonly ID = 'editor.contrib.contextmenu';
public static get(editor: ICodeEditor): ContextMenuController {
return editor.getContribution<ContextMenuController>(ContextMenuController.ID);
@@ -90,8 +90,18 @@ export class ContextMenuController implements IEditorContribution {
this._editor.focus();
// Ensure the cursor is at the position of the mouse click
if (e.target.position && !this._editor.getSelection().containsPosition(e.target.position)) {
this._editor.setPosition(e.target.position);
if (e.target.position) {
let hasSelectionAtPosition = false;
for (const selection of this._editor.getSelections()) {
if (selection.containsPosition(e.target.position)) {
hasSelectionAtPosition = true;
break;
}
}
if (!hasSelectionAtPosition) {
this._editor.setPosition(e.target.position);
}
}
// Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position
@@ -209,10 +219,6 @@ export class ContextMenuController implements IEditorContribution {
return this._keybindingService.lookupKeybinding(action.id);
}
public getId(): string {
return ContextMenuController.ID;
}
public dispose(): void {
if (this._contextMenuIsBeingShownCount > 0) {
this._contextViewService.hideContextView();
@@ -244,5 +250,5 @@ class ShowContextMenu extends EditorAction {
}
}
registerEditorContribution(ContextMenuController);
registerEditorContribution(ContextMenuController.ID, ContextMenuController);
registerEditorAction(ShowContextMenu);

View File

@@ -35,81 +35,83 @@ class CursorState {
}
}
export class CursorUndoController extends Disposable implements IEditorContribution {
export class CursorUndoRedoController extends Disposable implements IEditorContribution {
private static readonly ID = 'editor.contrib.cursorUndoController';
public static readonly ID = 'editor.contrib.cursorUndoRedoController';
public static get(editor: ICodeEditor): CursorUndoController {
return editor.getContribution<CursorUndoController>(CursorUndoController.ID);
public static get(editor: ICodeEditor): CursorUndoRedoController {
return editor.getContribution<CursorUndoRedoController>(CursorUndoRedoController.ID);
}
private readonly _editor: ICodeEditor;
private _isCursorUndo: boolean;
private _isCursorUndoRedo: boolean;
private _undoStack: CursorState[];
private _redoStack: CursorState[];
private _prevState: CursorState | null;
constructor(editor: ICodeEditor) {
super();
this._editor = editor;
this._isCursorUndo = false;
this._isCursorUndoRedo = false;
this._undoStack = [];
this._prevState = this._readState();
this._redoStack = [];
this._prevState = null;
this._register(editor.onDidChangeModel((e) => {
this._undoStack = [];
this._redoStack = [];
this._prevState = null;
}));
this._register(editor.onDidChangeModelContent((e) => {
this._undoStack = [];
this._redoStack = [];
this._prevState = null;
}));
this._register(editor.onDidChangeCursorSelection((e) => {
if (!this._isCursorUndo && this._prevState) {
this._undoStack.push(this._prevState);
if (this._undoStack.length > 50) {
// keep the cursor undo stack bounded
this._undoStack.shift();
const newState = new CursorState(this._editor.getSelections()!);
if (!this._isCursorUndoRedo && this._prevState) {
const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].equals(this._prevState));
if (!isEqualToLastUndoStack) {
this._undoStack.push(this._prevState);
this._redoStack = [];
if (this._undoStack.length > 50) {
// keep the cursor undo stack bounded
this._undoStack.shift();
}
}
}
this._prevState = this._readState();
this._prevState = newState;
}));
}
private _readState(): CursorState | null {
if (!this._editor.hasModel()) {
// no model => no state
return null;
}
return new CursorState(this._editor.getSelections());
}
public getId(): string {
return CursorUndoController.ID;
}
public cursorUndo(): void {
if (!this._editor.hasModel()) {
if (!this._editor.hasModel() || this._undoStack.length === 0) {
return;
}
const currState = new CursorState(this._editor.getSelections());
this._redoStack.push(new CursorState(this._editor.getSelections()));
this._applyState(this._undoStack.pop()!);
}
while (this._undoStack.length > 0) {
const prevState = this._undoStack.pop()!;
if (!prevState.equals(currState)) {
this._isCursorUndo = true;
this._editor.setSelections(prevState.selections);
this._editor.revealRangeInCenterIfOutsideViewport(prevState.selections[0], ScrollType.Smooth);
this._isCursorUndo = false;
return;
}
public cursorRedo(): void {
if (!this._editor.hasModel() || this._redoStack.length === 0) {
return;
}
this._undoStack.push(new CursorState(this._editor.getSelections()));
this._applyState(this._redoStack.pop()!);
}
private _applyState(state: CursorState): void {
this._isCursorUndoRedo = true;
this._editor.setSelections(state.selections);
this._editor.revealRangeInCenterIfOutsideViewport(state.selections[0], ScrollType.Smooth);
this._isCursorUndoRedo = false;
}
}
@@ -129,9 +131,25 @@ export class CursorUndo extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
CursorUndoController.get(editor).cursorUndo();
CursorUndoRedoController.get(editor).cursorUndo();
}
}
registerEditorContribution(CursorUndoController);
export class CursorRedo extends EditorAction {
constructor() {
super({
id: 'cursorRedo',
label: nls.localize('cursor.redo', "Soft Redo"),
alias: 'Soft Redo',
precondition: undefined
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
CursorUndoRedoController.get(editor).cursorRedo();
}
}
registerEditorContribution(CursorUndoRedoController.ID, CursorUndoRedoController);
registerEditorAction(CursorUndo);
registerEditorAction(CursorRedo);

View File

@@ -31,14 +31,14 @@ function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean {
export class DragAndDropController extends Disposable implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.dragAndDrop';
public static readonly ID = 'editor.contrib.dragAndDrop';
private readonly _editor: ICodeEditor;
private _dragSelection: Selection | null;
private _dndDecorationIds: string[];
private _mouseDown: boolean;
private _modifierPressed: boolean;
static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl;
static readonly TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl;
static get(editor: ICodeEditor): DragAndDropController {
return editor.getContribution<DragAndDropController>(DragAndDropController.ID);
@@ -219,10 +219,6 @@ export class DragAndDropController extends Disposable implements editorCommon.IE
target.type === MouseTargetType.GUTTER_LINE_DECORATIONS;
}
public getId(): string {
return DragAndDropController.ID;
}
public dispose(): void {
this._removeDecoration();
this._dragSelection = null;
@@ -232,4 +228,4 @@ export class DragAndDropController extends Disposable implements editorCommon.IE
}
}
registerEditorContribution(DragAndDropController);
registerEditorContribution(DragAndDropController.ID, DragAndDropController);

View File

@@ -35,7 +35,7 @@
}
.monaco-list .outline-element .outline-element-decoration.bubble {
font-family: octicons;
font-family: codicon;
font-size: 14px;
opacity: 0.4;
}

View File

@@ -6,13 +6,13 @@
import * as dom from 'vs/base/browser/dom';
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 { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree';
import { values } from 'vs/base/common/collections';
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, SymbolTag } from 'vs/editor/common/modes';
import { SymbolKind, SymbolKinds, SymbolTag } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { localize } from 'vs/nls';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
@@ -44,7 +44,7 @@ export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
}
export class OutlineGroupTemplate {
static id = 'OutlineGroupTemplate';
static readonly id = 'OutlineGroupTemplate';
constructor(
readonly labelContainer: HTMLElement,
readonly label: HighlightedLabel,
@@ -52,7 +52,7 @@ export class OutlineGroupTemplate {
}
export class OutlineElementTemplate {
static id = 'OutlineElementTemplate';
static readonly id = 'OutlineElementTemplate';
constructor(
readonly container: HTMLElement,
readonly iconLabel: IconLabel,
@@ -125,7 +125,7 @@ export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, Fuz
};
if (this._configurationService.getValue(OutlineConfigKeys.icons)) {
// add styles for the icons
options.extraClasses.push(`outline-element-icon ${symbolKindToCssClass(element.symbol.kind, true)}`);
options.extraClasses.push(`outline-element-icon ${SymbolKinds.toCssClassName(element.symbol.kind, true)}`);
}
if (element.symbol.tags.indexOf(SymbolTag.Deprecated) >= 0) {
options.extraClasses.push(`deprecated`);
@@ -168,7 +168,7 @@ export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, Fuz
} else {
dom.show(template.decoration);
dom.addClass(template.decoration, 'bubble');
template.decoration.innerText = '\uf052';
template.decoration.innerText = '\uea71';
template.decoration.title = localize('deep.problem', "Contains elements with problems");
template.decoration.style.setProperty('--outline-element-color', cssColor);
}
@@ -214,6 +214,31 @@ export const enum OutlineSortOrder {
ByKind
}
export class OutlineFilter implements ITreeFilter<OutlineItem> {
private readonly _filteredTypes = new Set<SymbolKind>();
constructor(
private readonly _prefix: string,
@IConfigurationService private readonly _configService: IConfigurationService,
) {
}
update() {
this._filteredTypes.clear();
for (const name of SymbolKinds.names()) {
if (!this._configService.getValue<boolean>(`${this._prefix}.${name}`)) {
this._filteredTypes.add(SymbolKinds.fromString(name) || -1);
}
}
}
filter(element: OutlineItem): boolean {
return !(element instanceof OutlineElement) || !this._filteredTypes.has(element.symbol.kind);
}
}
export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
private readonly _collator = new IdleValue<Intl.Collator>(() => new Intl.Collator(undefined, { numeric: true }));

View File

@@ -70,7 +70,7 @@ export interface IFindStartOptions {
export class CommonFindController extends Disposable implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.findController';
public static readonly ID = 'editor.contrib.findController';
protected _editor: ICodeEditor;
private readonly _findWidgetVisible: IContextKey<boolean>;
@@ -143,10 +143,6 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}
}
public getId(): string {
return CommonFindController.ID;
}
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
this.saveQueryState(e);
@@ -735,7 +731,7 @@ export class StartFindReplaceAction extends EditorAction {
}
}
registerEditorContribution(FindController);
registerEditorContribution(CommonFindController.ID, FindController);
registerEditorAction(StartFindAction);
registerEditorAction(StartFindWithSelectionAction);

View File

@@ -12,7 +12,7 @@ import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/commo
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 { Constants } from 'vs/editor/common/core/uint';
import { Constants } from 'vs/base/common/uint';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';

View File

@@ -89,7 +89,7 @@ suite('FindController', () => {
assert.ok(true);
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction();
// I select ABC on the first line
editor.setSelection(new Selection(1, 1, 1, 4));
@@ -115,7 +115,7 @@ suite('FindController', () => {
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let findState = findController.getState();
let nextMatchFindAction = new NextMatchFindAction();
@@ -141,7 +141,7 @@ suite('FindController', () => {
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let findState = findController.getState();
findState.change({ searchString: 'ABC' }, true);
@@ -161,7 +161,7 @@ suite('FindController', () => {
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let findState = findController.getState();
let startFindAction = new StartFindAction();
let nextMatchFindAction = new NextMatchFindAction();
@@ -215,7 +215,7 @@ suite('FindController', () => {
'import nls = require(\'vs/nls\');'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let nextMatchFindAction = new NextMatchFindAction();
editor.setPosition({
@@ -240,7 +240,7 @@ suite('FindController', () => {
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction();
let nextMatchFindAction = new NextMatchFindAction();
@@ -264,7 +264,7 @@ suite('FindController', () => {
'test',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let testRegexString = 'tes.';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let nextMatchFindAction = new NextMatchFindAction();
let startFindReplaceAction = new StartFindReplaceAction();
@@ -294,7 +294,7 @@ suite('FindController', () => {
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
@@ -322,7 +322,7 @@ suite('FindController', () => {
'HRESULT OnAmbientPropertyChange(DISPID dispid);'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction();
startFindAction.run(null, editor);
@@ -349,7 +349,7 @@ suite('FindController', () => {
'line3'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction();
startFindAction.run(null, editor);
@@ -376,7 +376,7 @@ suite('FindController', () => {
'([funny]'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
// toggle regex
@@ -403,7 +403,7 @@ suite('FindController', () => {
'([funny]'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let startFindAction = new StartFindAction();
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
@@ -454,7 +454,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false };
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let findState = findController.getState();
let startFindAction = new StartFindAction();
@@ -481,7 +481,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
let findState = findController.getState();
let startFindAction = new StartFindAction();
@@ -506,7 +506,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
findController.toggleRegex();
assert.equal(queryState['editor.isRegex'], true);
@@ -522,7 +522,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 1, 2, 1));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
findController.start({
forceRevealReplace: false,
@@ -545,7 +545,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 2));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
findController.start({
forceRevealReplace: false,
@@ -568,7 +568,7 @@ suite('FindController query options persistence', () => {
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 3));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
findController.start({
forceRevealReplace: false,

View File

@@ -183,6 +183,10 @@ suite('Replace Pattern test', () => {
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar-newabc'), 'Newfoo-Newbar-Newabc');
actual = ['Foo-Bar-abc'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'Newfoo-newbar');
actual = ['foo-Bar'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'newfoo-Newbar');
actual = ['foo-BAR'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'newfoo-NEWBAR');
actual = ['Foo_Bar'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_Newbar');
@@ -192,6 +196,10 @@ suite('Replace Pattern test', () => {
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_newbar');
actual = ['Foo_Bar-abc'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar-abc'), 'Newfoo_newbar-abc');
actual = ['foo_Bar'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'newfoo_Newbar');
actual = ['Foo_BAR'];
assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_NEWBAR');
});
test('preserve case', () => {
@@ -227,6 +235,14 @@ suite('Replace Pattern test', () => {
actual = replacePattern.buildReplaceString(['Foo-Bar-abc'], true);
assert.equal(actual, 'Newfoo-newbar');
replacePattern = parseReplaceString('newfoo-newbar');
actual = replacePattern.buildReplaceString(['foo-Bar'], true);
assert.equal(actual, 'newfoo-Newbar');
replacePattern = parseReplaceString('newfoo-newbar');
actual = replacePattern.buildReplaceString(['foo-BAR'], true);
assert.equal(actual, 'newfoo-NEWBAR');
replacePattern = parseReplaceString('newfoo_newbar');
actual = replacePattern.buildReplaceString(['Foo_Bar'], true);
assert.equal(actual, 'Newfoo_Newbar');
@@ -242,5 +258,13 @@ suite('Replace Pattern test', () => {
replacePattern = parseReplaceString('newfoo_newbar-abc');
actual = replacePattern.buildReplaceString(['Foo_Bar-abc'], true);
assert.equal(actual, 'Newfoo_newbar-abc');
replacePattern = parseReplaceString('newfoo_newbar');
actual = replacePattern.buildReplaceString(['foo_Bar'], true);
assert.equal(actual, 'newfoo_Newbar');
replacePattern = parseReplaceString('newfoo_newbar');
actual = replacePattern.buildReplaceString(['foo_BAR'], true);
assert.equal(actual, 'newfoo_NEWBAR');
});
});

View File

@@ -34,7 +34,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const CONTEXT_FOLDING_ENABLED = new RawContextKey<boolean>('foldingEnabled', false);
export const ID = 'editor.contrib.folding';
export interface RangeProvider {
readonly id: string;
@@ -50,11 +49,12 @@ interface FoldingStateMemento {
export class FoldingController extends Disposable implements IEditorContribution {
static MAX_FOLDING_REGIONS = 5000;
public static ID = 'editor.contrib.folding';
static readonly MAX_FOLDING_REGIONS = 5000;
public static get(editor: ICodeEditor): FoldingController {
return editor.getContribution<FoldingController>(ID);
return editor.getContribution<FoldingController>(FoldingController.ID);
}
private readonly editor: ICodeEditor;
@@ -134,10 +134,6 @@ export class FoldingController extends Disposable implements IEditorContribution
this.onModelChanged();
}
public getId(): string {
return ID;
}
/**
* Store view state.
*/
@@ -667,7 +663,7 @@ class ToggleFoldAction extends FoldingAction<void> {
precondition: CONTEXT_FOLDING_ENABLED,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K),
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_L),
weight: KeybindingWeight.EditorContrib
}
});
@@ -856,7 +852,7 @@ class FoldLevelAction extends FoldingAction<void> {
}
}
registerEditorContribution(FoldingController);
registerEditorContribution(FoldingController.ID, FoldingController);
registerEditorAction(UnfoldAction);
registerEditorAction(UnFoldRecursivelyAction);
registerEditorAction(FoldAction);

View File

@@ -10,18 +10,18 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class FoldingDecorationProvider implements IDecorationProvider {
private static COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
afterContentClassName: 'inline-folded',
linesDecorationsClassName: 'codicon codicon-chevron-right'
});
private static EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'codicon codicon-chevron-down'
});
private static EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'codicon codicon-chevron-down alwaysShowFoldIcons'
});

View File

@@ -28,7 +28,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
class FormatOnType implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.autoFormat';
public static readonly ID = 'editor.contrib.autoFormat';
private readonly _editor: ICodeEditor;
private readonly _callOnDispose = new DisposableStore();
@@ -45,10 +45,6 @@ class FormatOnType implements editorCommon.IEditorContribution {
this._callOnDispose.add(OnTypeFormattingEditProviderRegistry.onDidChange(this._update, this));
}
getId(): string {
return FormatOnType.ID;
}
dispose(): void {
this._callOnDispose.dispose();
this._callOnModel.dispose();
@@ -155,7 +151,7 @@ class FormatOnType implements editorCommon.IEditorContribution {
class FormatOnPaste implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.formatOnPaste';
public static readonly ID = 'editor.contrib.formatOnPaste';
private readonly _callOnDispose = new DisposableStore();
private readonly _callOnModel = new DisposableStore();
@@ -170,10 +166,6 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
this._callOnDispose.add(DocumentRangeFormattingEditProviderRegistry.onDidChange(this._update, this));
}
getId(): string {
return FormatOnPaste.ID;
}
dispose(): void {
this._callOnDispose.dispose();
this._callOnModel.dispose();
@@ -278,8 +270,8 @@ class FormatSelectionAction extends EditorAction {
}
}
registerEditorContribution(FormatOnType);
registerEditorContribution(FormatOnPaste);
registerEditorContribution(FormatOnType.ID, FormatOnType);
registerEditorContribution(FormatOnPaste.ID, FormatOnPaste);
registerEditorAction(FormatDocumentAction);
registerEditorAction(FormatSelectionAction);

View File

@@ -31,6 +31,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { ISymbolNavigationService } from 'vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { isEqual } from 'vs/base/common/resources';
export class DefinitionActionConfig {
@@ -84,7 +85,7 @@ export class DefinitionAction extends EditorAction {
}
const newLen = result.push(reference);
if (this._configuration.filterCurrent
&& reference.uri.toString() === model.uri.toString()
&& isEqual(reference.uri, model.uri)
&& Range.containsPosition(reference.range, pos)
&& idxOfCurrent === -1
) {
@@ -162,7 +163,7 @@ export class DefinitionAction extends EditorAction {
}
}
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise<ICodeEditor | undefined> {
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 = undefined;

View File

@@ -29,8 +29,8 @@ 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;
public static readonly ID = 'editor.contrib.gotodefinitionwithmouse';
static readonly MAX_SOURCE_PREVIEW_LINES = 8;
private readonly editor: ICodeEditor;
private readonly toUnhook = new DisposableStore();
@@ -220,7 +220,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
brackets.push(currentBracket);
} else {
const lastBracket = brackets[brackets.length - 1];
if (lastBracket.open === currentBracket.open && lastBracket.isOpen && !currentBracket.isOpen) {
if (lastBracket.open[0] === currentBracket.open[0] && lastBracket.isOpen && !currentBracket.isOpen) {
brackets.pop();
} else {
brackets.push(currentBracket);
@@ -295,16 +295,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor));
}
public getId(): string {
return GotoDefinitionWithMouseEditorContribution.ID;
}
public dispose(): void {
this.toUnhook.dispose();
}
}
registerEditorContribution(GotoDefinitionWithMouseEditorContribution);
registerEditorContribution(GotoDefinitionWithMouseEditorContribution.ID, GotoDefinitionWithMouseEditorContribution);
registerThemingParticipant((theme, collector) => {
const activeLinkForeground = theme.getColor(editorActiveLinkForeground);

View File

@@ -18,6 +18,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isEqual } from 'vs/base/common/resources';
export const ctxHasSymbols = new RawContextKey('hasSymbols', false);
@@ -92,7 +93,7 @@ class SymbolNavigationService implements ISymbolNavigationService {
let seenUri: boolean = false;
let seenPosition: boolean = false;
for (const reference of refModel.references) {
if (reference.uri.toString() === model.uri.toString()) {
if (isEqual(reference.uri, model.uri)) {
seenUri = true;
seenPosition = seenPosition || Range.containsPosition(reference.range, position);
} else if (seenUri) {

View File

@@ -20,12 +20,13 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
import { compare } from 'vs/base/common/strings';
import { binarySearch } from 'vs/base/common/arrays';
import { binarySearch, find } 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';
import { isEqual } from 'vs/base/common/resources';
class MarkerModel {
@@ -172,12 +173,7 @@ class MarkerModel {
}
public findMarkerAtPosition(pos: Position): IMarker | undefined {
for (const marker of this._markers) {
if (Range.containsPosition(marker, pos)) {
return marker;
}
}
return undefined;
return find(this._markers, marker => Range.containsPosition(marker, pos));
}
public get total() {
@@ -195,7 +191,7 @@ class MarkerModel {
export class MarkerController implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.markerController';
public static readonly ID = 'editor.contrib.markerController';
public static get(editor: ICodeEditor): MarkerController {
return editor.getContribution<MarkerController>(MarkerController.ID);
@@ -219,10 +215,6 @@ export class MarkerController implements editorCommon.IEditorContribution {
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
}
public getId(): string {
return MarkerController.ID;
}
public dispose(): void {
this._cleanUp();
this._disposeOnClose.dispose();
@@ -248,8 +240,8 @@ export class MarkerController implements editorCommon.IEditorContribution {
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); } })
new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-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 codicon-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);
@@ -310,7 +302,7 @@ export class MarkerController implements editorCommon.IEditorContribution {
}
private _onMarkerChanged(changedResources: URI[]): void {
let editorModel = this._editor.getModel();
const editorModel = this._editor.getModel();
if (!editorModel) {
return;
}
@@ -319,7 +311,7 @@ export class MarkerController implements editorCommon.IEditorContribution {
return;
}
if (!changedResources.some(r => editorModel!.uri.toString() === r.toString())) {
if (!changedResources.some(r => isEqual(editorModel.uri, r))) {
return;
}
this._model.setMarkers(this._getMarkers());
@@ -371,7 +363,7 @@ class MarkerNavigationAction extends EditorAction {
return Promise.resolve(undefined);
}
let editorModel = editor.getModel();
const editorModel = editor.getModel();
if (!editorModel) {
return Promise.resolve(undefined);
}
@@ -389,7 +381,7 @@ class MarkerNavigationAction extends EditorAction {
}
let newMarker = markers[idx];
if (newMarker.resource.toString() === editorModel!.uri.toString()) {
if (isEqual(newMarker.resource, editorModel.uri)) {
// the next `resource` is this resource which
// means we cycle within this file
model.move(this._isNext, true);
@@ -483,7 +475,7 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction {
}
}
registerEditorContribution(MarkerController);
registerEditorContribution(MarkerController.ID, MarkerController);
registerEditorAction(NextMarkerAction);
registerEditorAction(PrevMarkerAction);
registerEditorAction(NextMarkerInFilesAction);
@@ -522,4 +514,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem")
},
order: 2
});
});

View File

@@ -283,7 +283,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
: nls.localize('change', "{0} of {1} problem", markerIdx, markerCount);
this.setTitle(basename(model.uri), detail);
}
this._icon.className = SeverityIcon.className(MarkerSeverity.toSeverity(this._severity));
this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`;
this.editor.revealPositionInCenter(position, ScrollType.Smooth);
}

View File

@@ -7,7 +7,7 @@ import 'vs/css!./hover';
import * as nls from 'vs/nls';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
@@ -29,26 +29,26 @@ import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibi
export class ModesHoverController implements IEditorContribution {
private static readonly ID = 'editor.contrib.hover';
public static readonly ID = 'editor.contrib.hover';
private readonly _toUnhook = new DisposableStore();
private readonly _didChangeConfigurationHandler: IDisposable;
private _contentWidget: ModesContentHoverWidget | null;
private _glyphWidget: ModesGlyphHoverWidget | null;
private readonly _contentWidget = new MutableDisposable<ModesContentHoverWidget>();
private readonly _glyphWidget = new MutableDisposable<ModesGlyphHoverWidget>();
get contentWidget(): ModesContentHoverWidget {
if (!this._contentWidget) {
if (!this._contentWidget.value) {
this._createHoverWidgets();
}
return this._contentWidget!;
return this._contentWidget.value!;
}
get glyphWidget(): ModesGlyphHoverWidget {
if (!this._glyphWidget) {
if (!this._glyphWidget.value) {
this._createHoverWidgets();
}
return this._glyphWidget!;
return this._glyphWidget.value!;
}
private _isMouseDown: boolean;
@@ -69,8 +69,6 @@ export class ModesHoverController implements IEditorContribution {
) {
this._isMouseDown = false;
this._hoverClicked = false;
this._contentWidget = null;
this._glyphWidget = null;
this._hookEvents();
@@ -197,38 +195,29 @@ export class ModesHoverController implements IEditorContribution {
}
private _hideWidgets(): void {
if (!this._glyphWidget || !this._contentWidget || (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible())) {
if (!this._glyphWidget.value || !this._contentWidget.value || (this._isMouseDown && this._hoverClicked && this._contentWidget.value.isColorPickerVisible())) {
return;
}
this._glyphWidget!.hide();
this._contentWidget.hide();
this._glyphWidget.value.hide();
this._contentWidget.value.hide();
}
private _createHoverWidgets() {
this._contentWidget = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService);
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService);
this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
}
public showContentHover(range: Range, mode: HoverStartMode, focus: boolean): void {
this.contentWidget.startShowingAt(range, mode, focus);
}
public getId(): string {
return ModesHoverController.ID;
}
public dispose(): void {
this._unhookEvents();
this._toUnhook.dispose();
this._didChangeConfigurationHandler.dispose();
if (this._glyphWidget) {
this._glyphWidget.dispose();
}
if (this._contentWidget) {
this._contentWidget.dispose();
}
this._glyphWidget.dispose();
this._contentWidget.dispose();
}
}
@@ -269,7 +258,7 @@ class ShowHoverAction extends EditorAction {
}
}
registerEditorContribution(ModesHoverController);
registerEditorContribution(ModesHoverController.ID, ModesHoverController);
registerEditorAction(ShowHoverAction);
// theming

View File

@@ -38,6 +38,7 @@ import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
const $ = dom.$;
@@ -333,7 +334,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._colorPicker = null;
// update column from which to show
let renderColumn = Number.MAX_VALUE;
let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER;
let highlightRange: Range | null = messages[0].range ? Range.lift(messages[0].range) : null;
let fragment = document.createDocumentFragment();
let isEmptyHoverContent = true;
@@ -505,7 +506,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
e.stopPropagation();
e.preventDefault();
if (this._openerService) {
this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` })).catch(onUnexpectedError);
this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` }), { fromUserGesture: true }).catch(onUnexpectedError);
}
};
const messageElement = dom.append<HTMLAnchorElement>(relatedInfoContainer, $('span'));

View File

@@ -24,7 +24,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
class InPlaceReplaceController implements IEditorContribution {
private static readonly ID = 'editor.contrib.inPlaceReplaceController';
public static readonly ID = 'editor.contrib.inPlaceReplaceController';
static get(editor: ICodeEditor): InPlaceReplaceController {
return editor.getContribution<InPlaceReplaceController>(InPlaceReplaceController.ID);
@@ -51,10 +51,6 @@ class InPlaceReplaceController implements IEditorContribution {
public dispose(): void {
}
public getId(): string {
return InPlaceReplaceController.ID;
}
public run(source: string, up: boolean): Promise<void> | undefined {
// cancel any pending request
@@ -183,7 +179,7 @@ class InPlaceReplaceDown extends EditorAction {
}
}
registerEditorContribution(InPlaceReplaceController);
registerEditorContribution(InPlaceReplaceController.ID, InPlaceReplaceController);
registerEditorAction(InPlaceReplaceUp);
registerEditorAction(InPlaceReplaceDown);

View File

@@ -422,7 +422,7 @@ export class AutoIndentOnPasteCommand implements ICommand {
}
export class AutoIndentOnPaste implements IEditorContribution {
private static readonly ID = 'editor.contrib.autoIndentOnPaste';
public static readonly ID = 'editor.contrib.autoIndentOnPaste';
private readonly editor: ICodeEditor;
private readonly callOnDispose = new DisposableStore();
@@ -604,10 +604,6 @@ export class AutoIndentOnPaste implements IEditorContribution {
return false;
}
public getId(): string {
return AutoIndentOnPaste.ID;
}
public dispose(): void {
this.callOnDispose.dispose();
this.callOnModel.dispose();
@@ -681,7 +677,7 @@ export class IndentationToTabsCommand implements ICommand {
}
}
registerEditorContribution(AutoIndentOnPaste);
registerEditorContribution(AutoIndentOnPaste.ID, AutoIndentOnPaste);
registerEditorAction(IndentationToSpacesAction);
registerEditorAction(IndentationToTabsAction);
registerEditorAction(IndentUsingTabs);

View File

@@ -8,7 +8,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
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 { ReplaceCommand, ReplaceCommandThatPreservesSelection, ReplaceCommandThatSelectsText } from 'vs/editor/common/commands/replaceCommand';
import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
import { EditOperation } from 'vs/editor/common/core/editOperation';
@@ -97,6 +97,47 @@ class CopyLinesDownAction extends AbstractCopyLinesAction {
}
}
export class DuplicateSelectionAction extends EditorAction {
constructor() {
super({
id: 'editor.action.duplicateSelection',
label: nls.localize('duplicateSelection', "Duplicate Selection"),
alias: 'Duplicate Selection',
precondition: EditorContextKeys.writable,
menubarOpts: {
menuId: MenuId.MenubarSelectionMenu,
group: '2_line',
title: nls.localize({ key: 'miDuplicateSelection', comment: ['&& denotes a mnemonic'] }, "&&Duplicate Selection"),
order: 5
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
if (!editor.hasModel()) {
return;
}
const commands: ICommand[] = [];
const selections = editor.getSelections();
const model = editor.getModel();
for (const selection of selections) {
if (selection.isEmpty()) {
commands.push(new CopyLinesCommand(selection, true));
} else {
const insertSelection = new Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn);
commands.push(new ReplaceCommandThatSelectsText(insertSelection, model.getValueInRange(selection)));
}
}
editor.pushUndoStop();
editor.executeCommands(this.id, commands);
editor.pushUndoStop();
}
}
// move lines
abstract class AbstractMoveLinesAction extends EditorAction {
@@ -989,6 +1030,7 @@ export class TitleCaseAction extends AbstractCaseAction {
registerEditorAction(CopyLinesUpAction);
registerEditorAction(CopyLinesDownAction);
registerEditorAction(DuplicateSelectionAction);
registerEditorAction(MoveLinesUpAction);
registerEditorAction(MoveLinesDownAction);
registerEditorAction(SortLinesAscendingAction);

View File

@@ -3,9 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/copyLinesCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { DuplicateSelectionAction } from 'vs/editor/contrib/linesOperations/linesOperations';
function testCopyLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, null, selection, (sel) => new CopyLinesCommand(sel, true), expectedLines, expectedSelection);
@@ -195,3 +198,61 @@ suite('Editor Contrib - Copy Lines Command', () => {
);
});
});
suite('Editor Contrib - Duplicate Selection', () => {
const duplicateSelectionAction = new DuplicateSelectionAction();
function testDuplicateSelectionAction(lines: string[], selections: Selection[], expectedLines: string[], expectedSelections: Selection[]): void {
withTestCodeEditor(lines.join('\n'), {}, (editor, cursor) => {
editor.setSelections(selections);
duplicateSelectionAction.run(null!, editor, {});
assert.deepEqual(editor.getValue(), expectedLines.join('\n'));
assert.deepEqual(editor.getSelections()!.map(s => s.toString()), expectedSelections.map(s => s.toString()));
});
}
test('empty selection', function () {
testDuplicateSelectionAction(
[
'first',
'second line',
'third line',
'fourth line',
'fifth'
],
[new Selection(2, 2, 2, 2), new Selection(3, 2, 3, 2)],
[
'first',
'second line',
'second line',
'third line',
'third line',
'fourth line',
'fifth'
],
[new Selection(3, 2, 3, 2), new Selection(5, 2, 5, 2)]
);
});
test('with selection', function () {
testDuplicateSelectionAction(
[
'first',
'second line',
'third line',
'fourth line',
'fifth'
],
[new Selection(2, 1, 2, 4), new Selection(3, 1, 3, 4)],
[
'first',
'secsecond line',
'thithird line',
'fourth line',
'fifth'
],
[new Selection(2, 4, 2, 7), new Selection(3, 4, 3, 7)]
);
});
});

View File

@@ -44,8 +44,7 @@ function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString {
: nls.localize('links.navigate.kb.alt', "alt + click");
if (link.url) {
const hoverMessage = new MarkdownString().appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`);
hoverMessage.isTrusted = true;
const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`);
return hoverMessage;
} else {
return new MarkdownString().appendText(`${label} (${kb})`);
@@ -100,13 +99,13 @@ class LinkOccurrence {
class LinkDetector implements editorCommon.IEditorContribution {
private static readonly ID: string = 'editor.linkDetector';
public static readonly ID: string = 'editor.linkDetector';
public static get(editor: ICodeEditor): LinkDetector {
return editor.getContribution<LinkDetector>(LinkDetector.ID);
}
static RECOMPUTE_TIME = 1000; // ms
static readonly RECOMPUTE_TIME = 1000; // ms
private readonly editor: ICodeEditor;
private enabled: boolean;
@@ -171,10 +170,6 @@ class LinkDetector implements editorCommon.IEditorContribution {
this.beginCompute();
}
public getId(): string {
return LinkDetector.ID;
}
private onModelChanged(): void {
this.currentOccurrences = {};
this.activeLinkDecorationId = null;
@@ -284,10 +279,10 @@ class LinkDetector implements editorCommon.IEditorContribution {
if (!occurrence) {
return;
}
this.openLinkOccurrence(occurrence, mouseEvent.hasSideBySideModifier);
this.openLinkOccurrence(occurrence, mouseEvent.hasSideBySideModifier, true /* from user gesture */);
}
public openLinkOccurrence(occurrence: LinkOccurrence, openToSide: boolean): void {
public openLinkOccurrence(occurrence: LinkOccurrence, openToSide: boolean, fromUserGesture = false): void {
if (!this.openerService) {
return;
@@ -297,7 +292,7 @@ class LinkDetector implements editorCommon.IEditorContribution {
link.resolve(CancellationToken.None).then(uri => {
// open the uri
return this.openerService.open(uri, { openToSide });
return this.openerService.open(uri, { openToSide, fromUserGesture });
}, err => {
const messageOrError =
@@ -391,7 +386,7 @@ class OpenLinkAction extends EditorAction {
}
}
registerEditorContribution(LinkDetector);
registerEditorContribution(LinkDetector.ID, LinkDetector);
registerEditorAction(OpenLinkAction);
registerThemingParticipant((theme, collector) => {

View File

@@ -71,7 +71,7 @@ export class MarkdownRenderer extends Disposable {
// ignore
}
if (uri && this._openerService) {
this._openerService.open(uri).catch(onUnexpectedError);
this._openerService.open(uri, { fromUserGesture: true }).catch(onUnexpectedError);
}
},
disposeables

View File

@@ -21,20 +21,16 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
export class MessageController extends Disposable implements editorCommon.IEditorContribution {
private static readonly _id = 'editor.contrib.messageController';
public static readonly ID = 'editor.contrib.messageController';
static MESSAGE_VISIBLE = new RawContextKey<boolean>('messageVisible', false);
static readonly MESSAGE_VISIBLE = new RawContextKey<boolean>('messageVisible', false);
static get(editor: ICodeEditor): MessageController {
return editor.getContribution<MessageController>(MessageController._id);
return editor.getContribution<MessageController>(MessageController.ID);
}
private readonly closeTimeout = 3000; // close after 3s
getId(): string {
return MessageController._id;
}
private readonly _editor: ICodeEditor;
private readonly _visible: IContextKey<boolean>;
private readonly _messageWidget = this._register(new MutableDisposable<MessageWidget>());
@@ -184,7 +180,7 @@ class MessageWidget implements IContentWidget {
}
}
registerEditorContribution(MessageController);
registerEditorContribution(MessageController.ID, MessageController);
registerThemingParticipant((theme, collector) => {
const border = theme.getColor(inputValidationInfoBorder);

View File

@@ -14,7 +14,7 @@ import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/comm
import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { Constants } from 'vs/editor/common/core/uint';
import { Constants } from 'vs/base/common/uint';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { FindMatch, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
@@ -423,7 +423,7 @@ export class MultiCursorSession {
export class MultiCursorSelectionController extends Disposable implements IEditorContribution {
private static readonly ID = 'editor.contrib.multiCursorController';
public static readonly ID = 'editor.contrib.multiCursorController';
private readonly _editor: ICodeEditor;
private _ignoreSelectionChange: boolean;
@@ -446,10 +446,6 @@ export class MultiCursorSelectionController extends Disposable implements IEdito
super.dispose();
}
public getId(): string {
return MultiCursorSelectionController.ID;
}
private _beginSessionIfNeeded(findController: CommonFindController): void {
if (!this._session) {
// Create a new session
@@ -798,7 +794,7 @@ class SelectionHighlighterState {
}
export class SelectionHighlighter extends Disposable implements IEditorContribution {
private static readonly ID = 'editor.contrib.selectionHighlighter';
public static readonly ID = 'editor.contrib.selectionHighlighter';
private readonly editor: ICodeEditor;
private _isEnabled: boolean;
@@ -848,10 +844,6 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
}));
}
public getId(): string {
return SelectionHighlighter.ID;
}
private _update(): void {
this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor));
}
@@ -1041,8 +1033,8 @@ function getValueInRange(model: ITextModel, range: Range, toLowerCase: boolean):
return (toLowerCase ? text.toLowerCase() : text);
}
registerEditorContribution(MultiCursorSelectionController);
registerEditorContribution(SelectionHighlighter);
registerEditorContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
registerEditorContribution(SelectionHighlighter.ID, SelectionHighlighter);
registerEditorAction(InsertCursorAbove);
registerEditorAction(InsertCursorBelow);

View File

@@ -79,8 +79,8 @@ suite('Multicursor selection', () => {
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
let selectHighlightsAction = new SelectHighlightsAction();
editor.setSelection(new Selection(2, 9, 2, 16));
@@ -109,8 +109,8 @@ suite('Multicursor selection', () => {
'nothing'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
let selectHighlightsAction = new SelectHighlightsAction();
editor.setSelection(new Selection(1, 1, 1, 1));
@@ -143,8 +143,8 @@ suite('Multicursor selection', () => {
'rty'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(2, 1, 3, 4));
@@ -171,8 +171,8 @@ suite('Multicursor selection', () => {
'abcabc',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(1, 1, 1, 4));
@@ -228,8 +228,8 @@ suite('Multicursor selection', () => {
editor.getModel()!.setEOL(EndOfLineSequence.CRLF);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(2, 1, 3, 4));
@@ -251,8 +251,8 @@ suite('Multicursor selection', () => {
function testMulticursor(text: string[], callback: (editor: TestCodeEditor, findController: CommonFindController) => void): void {
withTestCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController);
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
callback(editor, findController);

View File

@@ -20,7 +20,7 @@ import { TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsM
class ParameterHintsController extends Disposable implements IEditorContribution {
private static readonly ID = 'editor.controller.parameterHints';
public static readonly ID = 'editor.controller.parameterHints';
public static get(editor: ICodeEditor): ParameterHintsController {
return editor.getContribution<ParameterHintsController>(ParameterHintsController.ID);
@@ -35,10 +35,6 @@ class ParameterHintsController extends Disposable implements IEditorContribution
this.widget = this._register(instantiationService.createInstance(ParameterHintsWidget, this.editor));
}
getId(): string {
return ParameterHintsController.ID;
}
cancel(): void {
this.widget.cancel();
}
@@ -82,7 +78,7 @@ export class TriggerParameterHintsAction extends EditorAction {
}
}
registerEditorContribution(ParameterHintsController);
registerEditorContribution(ParameterHintsController.ID, ParameterHintsController);
registerEditorAction(TriggerParameterHintsAction);
const weight = KeybindingWeight.EditorContrib + 75;

View File

@@ -55,6 +55,7 @@ export class ParameterHintsModel extends Disposable {
private readonly editor: ICodeEditor;
private triggerOnType = false;
private _state: ParameterHintState.State = ParameterHintState.Default;
private _pendingTriggers: TriggerContext[] = [];
private readonly _lastSignatureHelpResult = this._register(new MutableDisposable<modes.SignatureHelpResult>());
private triggerChars = new CharacterSet();
private retriggerChars = new CharacterSet();
@@ -109,13 +110,12 @@ export class ParameterHintsModel extends Disposable {
}
const triggerId = ++this.triggerId;
this.throttledDelayer.trigger(
() => this.doTrigger({
triggerKind: context.triggerKind,
triggerCharacter: context.triggerCharacter,
isRetrigger: this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending,
activeSignatureHelp: this.state.type === ParameterHintState.Type.Active ? this.state.hints : undefined
}, triggerId), delay).then(undefined, onUnexpectedError);
this._pendingTriggers.push(context);
this.throttledDelayer.trigger(() => {
return this.doTrigger(triggerId);
}, delay)
.catch(onUnexpectedError);
}
public next(): void {
@@ -165,11 +165,28 @@ export class ParameterHintsModel extends Disposable {
this._onChangedHints.fire(this.state.hints);
}
private doTrigger(triggerContext: modes.SignatureHelpContext, triggerId: number): Promise<boolean> {
private async doTrigger(triggerId: number): Promise<boolean> {
const isRetrigger = this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending;
const activeSignatureHelp = this.state.type === ParameterHintState.Type.Active ? this.state.hints : undefined;
this.cancel(true);
if (this._pendingTriggers.length === 0) {
return false;
}
const context: TriggerContext = this._pendingTriggers.reduce(mergeTriggerContexts);
this._pendingTriggers = [];
const triggerContext = {
triggerKind: context.triggerKind,
triggerCharacter: context.triggerCharacter,
isRetrigger: isRetrigger,
activeSignatureHelp: activeSignatureHelp
};
if (!this.editor.hasModel()) {
return Promise.resolve(false);
return false;
}
const model = this.editor.getModel();
@@ -178,19 +195,18 @@ export class ParameterHintsModel extends Disposable {
this.state = new ParameterHintState.Pending(createCancelablePromise(token =>
provideSignatureHelp(model, position, triggerContext, token)));
return this.state.request.then(result => {
try {
const result = await this.state.request;
// Check that we are still resolving the correct signature help
if (triggerId !== this.triggerId) {
if (result) {
result.dispose();
}
result?.dispose();
return false;
}
if (!result || !result.value.signatures || result.value.signatures.length === 0) {
if (result) {
result.dispose();
}
result?.dispose();
this._lastSignatureHelpResult.clear();
this.cancel();
return false;
@@ -200,13 +216,13 @@ export class ParameterHintsModel extends Disposable {
this._onChangedHints.fire(this.state.hints);
return true;
}
}).catch(error => {
} catch (error) {
if (triggerId === this.triggerId) {
this.state = ParameterHintState.Default;
}
onUnexpectedError(error);
return false;
});
}
}
private get isTriggered(): boolean {
@@ -284,3 +300,19 @@ export class ParameterHintsModel extends Disposable {
super.dispose();
}
}
function mergeTriggerContexts(previous: TriggerContext, current: TriggerContext) {
switch (current.triggerKind) {
case modes.SignatureHelpTriggerKind.Invoke:
// Invoke overrides previous triggers.
return current;
case modes.SignatureHelpTriggerKind.ContentChange:
// Ignore content changes triggers
return previous;
case modes.SignatureHelpTriggerKind.TriggerCharacter:
default:
return current;
}
}

View File

@@ -34,13 +34,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
private readonly model = this._register(new MutableDisposable<ParameterHintsModel>());
private readonly keyVisible: IContextKey<boolean>;
private readonly keyMultipleSignatures: IContextKey<boolean>;
private element: HTMLElement;
private signature: HTMLElement;
private docs: HTMLElement;
private overloads: HTMLElement;
private visible: boolean;
private announcedLabel: string | null;
private scrollbar: DomScrollableElement;
private domNodes?: {
readonly element: HTMLElement;
readonly signature: HTMLElement;
readonly docs: HTMLElement;
readonly overloads: HTMLElement;
readonly scrollbar: DomScrollableElement;
};
private visible: boolean = false;
private announcedLabel: string | null = null;
// Editor.IContentWidget.allowEditorOverflow
allowEditorOverflow = true;
@@ -56,7 +60,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
this.model.value = new ParameterHintsModel(editor);
this.keyVisible = Context.Visible.bindTo(contextKeyService);
this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService);
this.visible = false;
this._register(this.model.value.onChangedHints(newParameterHints => {
if (newParameterHints) {
@@ -69,8 +72,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}
private createParamaterHintDOMNodes() {
this.element = $('.editor-widget.parameter-hints-widget');
const wrapper = dom.append(this.element, $('.wrapper'));
const element = $('.editor-widget.parameter-hints-widget');
const wrapper = dom.append(element, $('.wrapper'));
wrapper.tabIndex = -1;
const buttons = dom.append(wrapper, $('.buttons'));
@@ -83,21 +86,29 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
const onNextClick = stop(domEvent(next, 'click'));
this._register(onNextClick(this.next, this));
this.overloads = dom.append(wrapper, $('.overloads'));
const overloads = dom.append(wrapper, $('.overloads'));
const body = $('.body');
this.scrollbar = new DomScrollableElement(body, {});
this._register(this.scrollbar);
wrapper.appendChild(this.scrollbar.getDomNode());
const scrollbar = new DomScrollableElement(body, {});
this._register(scrollbar);
wrapper.appendChild(scrollbar.getDomNode());
this.signature = dom.append(body, $('.signature'));
const signature = dom.append(body, $('.signature'));
const docs = dom.append(body, $('.docs'));
this.docs = dom.append(body, $('.docs'));
element.style.userSelect = 'text';
this.domNodes = {
element,
signature,
overloads,
docs,
scrollbar,
};
this.editor.addContentWidget(this);
this.hide();
this.element.style.userSelect = 'text';
this._register(this.editor.onDidChangeCursorSelection(e => {
if (this.visible) {
this.editor.layoutContentWidget(this);
@@ -105,8 +116,11 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}));
const updateFont = () => {
if (!this.domNodes) {
return;
}
const fontInfo = this.editor.getOption(EditorOption.fontInfo);
this.element.style.fontSize = `${fontInfo.fontSize}px`;
this.domNodes.element.style.fontSize = `${fontInfo.fontSize}px`;
};
updateFont();
@@ -124,13 +138,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
return;
}
if (!this.element) {
if (!this.domNodes) {
this.createParamaterHintDOMNodes();
}
this.keyVisible.set(true);
this.visible = true;
setTimeout(() => dom.addClass(this.element, 'visible'), 100);
setTimeout(() => {
if (this.domNodes) {
dom.addClass(this.domNodes.element, 'visible');
}
}, 100);
this.editor.layoutContentWidget(this);
}
@@ -139,14 +157,12 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
return;
}
if (!this.element) {
this.createParamaterHintDOMNodes();
}
this.keyVisible.reset();
this.visible = false;
this.announcedLabel = null;
dom.removeClass(this.element, 'visible');
if (this.domNodes) {
dom.removeClass(this.domNodes.element, 'visible');
}
this.editor.layoutContentWidget(this);
}
@@ -161,12 +177,16 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}
private render(hints: modes.SignatureHelp): void {
if (!this.domNodes) {
return;
}
const multiple = hints.signatures.length > 1;
dom.toggleClass(this.element, 'multiple', multiple);
dom.toggleClass(this.domNodes.element, 'multiple', multiple);
this.keyMultipleSignatures.set(multiple);
this.signature.innerHTML = '';
this.docs.innerHTML = '';
this.domNodes.signature.innerHTML = '';
this.domNodes.docs.innerHTML = '';
const signature = hints.signatures[hints.activeSignature];
@@ -174,7 +194,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
return;
}
const code = dom.append(this.signature, $('.code'));
const code = dom.append(this.domNodes.signature, $('.code'));
const hasParameters = signature.parameters.length > 0;
const fontInfo = this.editor.getOption(EditorOption.fontInfo);
@@ -203,17 +223,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
this.renderDisposeables.add(renderedContents);
documentation.appendChild(renderedContents.element);
}
dom.append(this.docs, $('p', {}, documentation));
dom.append(this.domNodes.docs, $('p', {}, documentation));
}
if (signature.documentation === undefined) { /** no op */ }
else if (typeof signature.documentation === 'string') {
dom.append(this.docs, $('p', {}, signature.documentation));
dom.append(this.domNodes.docs, $('p', {}, signature.documentation));
} else {
const renderedContents = this.markdownRenderer.render(signature.documentation);
dom.addClass(renderedContents.element, 'markdown-docs');
this.renderDisposeables.add(renderedContents);
dom.append(this.docs, renderedContents.element);
dom.append(this.domNodes.docs, renderedContents.element);
}
let hasDocs = false;
@@ -230,8 +250,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
hasDocs = true;
}
dom.toggleClass(this.signature, 'has-docs', hasDocs);
dom.toggleClass(this.docs, 'empty', !hasDocs);
dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs);
dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs);
let currentOverload = String(hints.activeSignature + 1);
@@ -239,7 +259,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
currentOverload += `/${hints.signatures.length}`;
}
this.overloads.textContent = currentOverload;
this.domNodes.overloads.textContent = currentOverload;
if (activeParameter) {
const labelToAnnounce = this.getParameterLabel(signature, hints.activeParameter);
@@ -253,7 +273,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}
this.editor.layoutContentWidget(this);
this.scrollbar.scanDomNode();
this.domNodes.scrollbar.scanDomNode();
}
private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, currentParameter: number): void {
@@ -317,7 +337,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}
getDomNode(): HTMLElement {
return this.element;
if (!this.domNodes) {
this.createParamaterHintDOMNodes();
}
return this.domNodes!.element;
}
getId(): string {
@@ -331,10 +354,13 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget,
}
private updateMaxHeight(): void {
if (!this.domNodes) {
return;
}
const height = Math.max(this.editor.getLayoutInfo().height / 4, 250);
const maxHeight = `${height}px`;
this.element.style.maxHeight = maxHeight;
const wrapper = this.element.getElementsByClassName('wrapper') as HTMLCollectionOf<HTMLElement>;
this.domNodes.element.style.maxHeight = maxHeight;
const wrapper = this.domNodes.element.getElementsByClassName('wrapper') as HTMLCollectionOf<HTMLElement>;
if (wrapper.length) {
wrapper[0].style.maxHeight = maxHeight;
}

View File

@@ -96,24 +96,29 @@ suite('ParameterHintsModel', () => {
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.activeSignatureHelp, undefined);
try {
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.activeSignatureHelp, undefined);
// Retrigger
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50);
} else {
assert.strictEqual(invokeCount, 2);
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.isRetrigger, true);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.activeSignatureHelp, emptySigHelp);
// Retrigger
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50);
} else {
assert.strictEqual(invokeCount, 2);
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.isRetrigger, true);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.activeSignatureHelp, emptySigHelp);
done();
done();
}
return emptySigHelpResult;
} catch (err) {
console.error(err);
throw err;
}
return emptySigHelpResult;
}
}));
@@ -133,25 +138,30 @@ suite('ParameterHintsModel', () => {
signatureHelpRetriggerCharacters = [];
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.activeSignatureHelp, undefined);
try {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.activeSignatureHelp, undefined);
// Cancel and retrigger
hintModel.cancel();
editor.trigger('keyboard', Handler.Type, { text: triggerChar });
} else {
assert.strictEqual(invokeCount, 2);
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, true);
assert.strictEqual(context.activeSignatureHelp, undefined);
done();
// Cancel and retrigger
hintModel.cancel();
editor.trigger('keyboard', Handler.Type, { text: triggerChar });
} else {
assert.strictEqual(invokeCount, 2);
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
assert.strictEqual(context.isRetrigger, true);
assert.strictEqual(context.activeSignatureHelp, undefined);
done();
}
return emptySigHelpResult;
} catch (err) {
console.error(err);
throw err;
}
return emptySigHelpResult;
}
}));
@@ -168,19 +178,24 @@ suite('ParameterHintsModel', () => {
signatureHelpRetriggerCharacters = [];
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext) {
++invokeCount;
try {
++invokeCount;
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.triggerCharacter, 'c');
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.isRetrigger, false);
assert.strictEqual(context.triggerCharacter, 'c');
// Give some time to allow for later triggers
setTimeout(() => {
assert.strictEqual(invokeCount, 1);
// Give some time to allow for later triggers
setTimeout(() => {
assert.strictEqual(invokeCount, 1);
done();
}, 50);
return undefined;
done();
}, 50);
return undefined;
} catch (err) {
console.error(err);
throw err;
}
}
}));
@@ -199,23 +214,28 @@ suite('ParameterHintsModel', () => {
signatureHelpRetriggerCharacters = [];
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, 'a');
try {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, 'a');
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: 'b' }), 50);
} else if (invokeCount === 2) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.ok(context.isRetrigger);
assert.strictEqual(context.triggerCharacter, 'b');
done();
} else {
assert.fail('Unexpected invoke');
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: 'b' }), 50);
} else if (invokeCount === 2) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.ok(context.isRetrigger);
assert.strictEqual(context.triggerCharacter, 'b');
done();
} else {
assert.fail('Unexpected invoke');
}
return emptySigHelpResult;
} catch (err) {
console.error(err);
throw err;
}
return emptySigHelpResult;
}
}));
@@ -234,29 +254,34 @@ suite('ParameterHintsModel', () => {
provideSignatureHelp(_model: ITextModel, _position: Position, token: CancellationToken): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
const count = invokeCount++;
token.onCancellationRequested(() => { didRequestCancellationOf = count; });
try {
const count = invokeCount++;
token.onCancellationRequested(() => { didRequestCancellationOf = count; });
// retrigger on first request
if (count === 0) {
hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0);
// retrigger on first request
if (count === 0) {
hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0);
}
return new Promise<modes.SignatureHelpResult>(resolve => {
setTimeout(() => {
resolve({
value: {
signatures: [{
label: '' + count,
parameters: []
}],
activeParameter: 0,
activeSignature: 0
},
dispose: () => { }
});
}, 100);
});
} catch (err) {
console.error(err);
throw err;
}
return new Promise<modes.SignatureHelpResult>(resolve => {
setTimeout(() => {
resolve({
value: {
signatures: [{
label: '' + count,
parameters: []
}],
activeParameter: 0,
activeSignature: 0
},
dispose: () => { }
});
}, 100);
});
}
};
@@ -290,23 +315,28 @@ suite('ParameterHintsModel', () => {
signatureHelpRetriggerCharacters = [retriggerChar];
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
try {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerChar);
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerChar }), 50);
} else if (invokeCount === 2) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.ok(context.isRetrigger);
assert.strictEqual(context.triggerCharacter, retriggerChar);
done();
} else {
assert.fail('Unexpected invoke');
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerChar }), 50);
} else if (invokeCount === 2) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.ok(context.isRetrigger);
assert.strictEqual(context.triggerCharacter, retriggerChar);
done();
} else {
assert.fail('Unexpected invoke');
}
return emptySigHelpResult;
} catch (err) {
console.error(err);
throw err;
}
return emptySigHelpResult;
}
}));
@@ -332,26 +362,31 @@ suite('ParameterHintsModel', () => {
signatureHelpRetriggerCharacters = [];
async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise<modes.SignatureHelpResult | undefined> {
if (!context.isRetrigger) {
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50);
try {
if (!context.isRetrigger) {
// retrigger after delay for widget to show up
setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50);
return {
value: {
activeParameter: 0,
activeSignature: 0,
signatures: [{
label: firstProviderId,
parameters: [
{ label: paramterLabel }
]
}]
},
dispose: () => { }
};
return {
value: {
activeParameter: 0,
activeSignature: 0,
signatures: [{
label: firstProviderId,
parameters: [
{ label: paramterLabel }
]
}]
},
dispose: () => { }
};
}
return undefined;
} catch (err) {
console.error(err);
throw err;
}
return undefined;
}
}));
@@ -390,6 +425,43 @@ suite('ParameterHintsModel', () => {
assert.strictEqual(secondHint.activeSignature, 1);
assert.strictEqual(secondHint.signatures[0].parameters[0].label, paramterLabel);
});
test('Quick typing should use the first trigger character', async () => {
const editor = createMockEditor('');
const model = new ParameterHintsModel(editor, 50);
disposables.add(model);
const triggerCharacter = 'a';
let invokeCount = 0;
disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider {
signatureHelpTriggerCharacters = [triggerCharacter];
signatureHelpRetriggerCharacters = [];
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
try {
++invokeCount;
if (invokeCount === 1) {
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
assert.strictEqual(context.triggerCharacter, triggerCharacter);
} else {
assert.fail('Unexpected invoke');
}
return emptySigHelpResult;
} catch (err) {
console.error(err);
throw err;
}
}
}));
editor.trigger('keyboard', Handler.Type, { text: triggerCharacter });
editor.trigger('keyboard', Handler.Type, { text: 'x' });
await getNextHint(model);
});
});
function getNextHint(model: ParameterHintsModel) {

View File

@@ -52,7 +52,7 @@
background-position: center center;
}
.monaco-editor .peekview-widget .head .peekview-actions > .monaco-action-bar .action-label.octicon {
.monaco-editor .peekview-widget .head .peekview-actions > .monaco-action-bar .action-label.codicon {
margin: 0;
}
@@ -60,3 +60,7 @@
border-top: 1px solid;
position: relative;
}
.monaco-editor .peekview-widget .head .peekview-title .codicon {
margin-right: 4px;
}

View File

@@ -57,8 +57,8 @@ export namespace PeekContext {
export const notInPeekEditor: ContextKeyExpr = inPeekEditor.toNegated();
}
export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | undefined {
const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | null {
let editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (editor instanceof EmbeddedCodeEditorWidget) {
return editor.getParentEditor();
}

View File

@@ -37,7 +37,7 @@ export const defaultReferenceSearchOptions: RequestOptions = {
export class ReferenceController implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.referenceController';
public static readonly ID = 'editor.contrib.referenceController';
constructor(
editor: ICodeEditor,
@@ -50,10 +50,6 @@ export class ReferenceController implements editorCommon.IEditorContribution {
public dispose(): void {
}
public getId(): string {
return ReferenceController.ID;
}
}
export class ReferenceAction extends EditorAction {
@@ -93,7 +89,7 @@ export class ReferenceAction extends EditorAction {
}
}
registerEditorContribution(ReferenceController);
registerEditorContribution(ReferenceController.ID, ReferenceController);
registerEditorAction(ReferenceAction);

View File

@@ -30,7 +30,7 @@ export interface RequestOptions {
export abstract class ReferencesController implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.referencesController';
public static readonly ID = 'editor.contrib.referencesController';
private readonly _disposables = new DisposableStore();
private readonly _editor: ICodeEditor;
@@ -59,10 +59,6 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
this._referenceSearchVisible = ctxReferenceSearchVisible.bindTo(contextKeyService);
}
public getId(): string {
return ReferencesController.ID;
}
public dispose(): void {
this._referenceSearchVisible.reset();
dispose(this._disposables);

View File

@@ -15,6 +15,7 @@ import { Location, LocationLink } from 'vs/editor/common/modes';
import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { Position } from 'vs/editor/common/core/position';
import { IMatch } from 'vs/base/common/filters';
import { Constants } from 'vs/base/common/uint';
export class OneReference {
readonly id: string;
@@ -72,7 +73,7 @@ export class FilePreview implements IDisposable {
const { startLineNumber, startColumn, endLineNumber, endColumn } = range;
const word = model.getWordUntilPosition({ lineNumber: startLineNumber, column: startColumn - n });
const beforeRange = new Range(startLineNumber, word.startColumn, startLineNumber, startColumn);
const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Number.MAX_VALUE);
const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
const before = model.getValueInRange(beforeRange).replace(/^\s+/, '');
const inside = model.getValueInRange(range);

View File

@@ -10,7 +10,7 @@ import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable, IReference, DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { basenameOrAuthority, dirname } from 'vs/base/common/resources';
import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources';
import 'vs/css!./media/referencesWidget';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
@@ -66,7 +66,7 @@ class DecorationsManager implements IDisposable {
const model = this._editor.getModel();
if (model) {
for (const ref of this._model.groups) {
if (ref.uri.toString() === model.uri.toString()) {
if (isEqual(ref.uri, model.uri)) {
this._addDecorations(ref);
return;
}
@@ -158,8 +158,8 @@ class DecorationsManager implements IDisposable {
}
export class LayoutData {
ratio: number = 0.7;
heightInLines: number = 18;
public ratio: number = 0.7;
public heightInLines: number = 18;
static fromJSON(raw: string): LayoutData {
let ratio: number | undefined;
@@ -179,9 +179,9 @@ export class LayoutData {
}
export interface SelectionEvent {
kind: 'goto' | 'show' | 'side' | 'open';
source: 'editor' | 'tree' | 'title';
element?: Location;
readonly kind: 'goto' | 'show' | 'side' | 'open';
readonly source: 'editor' | 'tree' | 'title';
readonly element?: Location;
}
export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey<boolean>('referenceSearchTreeFocused', true);
@@ -366,21 +366,6 @@ export class ReferenceWidget extends PeekViewWidget {
this._tree.onDidChangeFocus(e => {
onEvent(e.elements[0], 'show');
});
this._tree.onDidChangeSelection(e => {
let aside = false;
let goto = false;
if (e.browserEvent instanceof KeyboardEvent) {
// todo@joh make this a command
goto = true;
}
if (aside) {
onEvent(e.elements[0], 'side');
} else if (goto) {
onEvent(e.elements[0], 'goto');
} else {
onEvent(e.elements[0], 'show');
}
});
this._tree.onDidOpen(e => {
const aside = (e.browserEvent instanceof MouseEvent) && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey);
let goto = !e.browserEvent || ((e.browserEvent instanceof MouseEvent) && e.browserEvent.detail === 2);

View File

@@ -20,15 +20,15 @@ import { Position, IPosition } from 'vs/editor/common/core/position';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { Range } from 'vs/editor/common/core/range';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/browser/core/editorState';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable } from 'vs/base/common/lifecycle';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IdleValue, raceCancellation } from 'vs/base/common/async';
import { withNullAsUndefined } from 'vs/base/common/types';
class RenameSkeleton {
@@ -95,21 +95,17 @@ export async function rename(model: ITextModel, position: Position, newName: str
// --- register actions and commands
class RenameController extends Disposable implements IEditorContribution {
class RenameController implements IEditorContribution {
private static readonly ID = 'editor.contrib.renameController';
public static readonly ID = 'editor.contrib.renameController';
public static get(editor: ICodeEditor): RenameController {
static get(editor: ICodeEditor): RenameController {
return editor.getContribution<RenameController>(RenameController.ID);
}
private _renameInputField?: RenameInputField;
private _renameOperationIdPool = 1;
private _activeRename?: {
readonly id: number;
readonly operation: CancelablePromise<void>;
};
private readonly _renameInputField: IdleValue<RenameInputField>;
private readonly _dispoableStore = new DisposableStore();
private _cts: CancellationTokenSource = new CancellationTokenSource();
constructor(
private readonly editor: ICodeEditor,
@@ -119,37 +115,18 @@ class RenameController extends Disposable implements IEditorContribution {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
) {
super();
this._register(this.editor.onDidChangeModel(() => this.onModelChanged()));
this._register(this.editor.onDidChangeModelLanguage(() => this.onModelChanged()));
this._register(this.editor.onDidChangeCursorSelection(() => this.onModelChanged()));
this._renameInputField = new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService)));
}
private get renameInputField(): RenameInputField {
if (!this._renameInputField) {
this._renameInputField = this._register(new RenameInputField(this.editor, this._themeService, this._contextKeyService));
}
return this._renameInputField;
}
getId(): string {
return RenameController.ID;
dispose(): void {
this._dispoableStore.dispose();
this._cts.dispose(true);
}
async run(): Promise<void> {
if (this._activeRename) {
this._activeRename.operation.cancel();
}
const id = this._renameOperationIdPool++;
this._activeRename = {
id,
operation: createCancelablePromise(token => this.doRename(token, id))
};
return this._activeRename.operation;
}
this._cts.dispose(true);
private async doRename(token: CancellationToken, id: number): Promise<void> {
if (!this.editor.hasModel()) {
return undefined;
}
@@ -161,9 +138,12 @@ class RenameController extends Disposable implements IEditorContribution {
return undefined;
}
this._cts = new EditorStateCancellationTokenSource(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value);
// resolve rename location
let loc: RenameLocation & Rejection | undefined;
try {
const resolveLocationOperation = skeleton.resolveRenameLocation(token);
const resolveLocationOperation = skeleton.resolveRenameLocation(this._cts.token);
this._progressService.showWhile(resolveLocationOperation, 250);
loc = await resolveLocationOperation;
} catch (e) {
@@ -180,10 +160,11 @@ class RenameController extends Disposable implements IEditorContribution {
return undefined;
}
if (!this._activeRename || this._activeRename.id !== id) {
if (this._cts.token.isCancellationRequested) {
return undefined;
}
// do rename at location
let selection = this.editor.getSelection();
let selectionStart = 0;
let selectionEnd = loc.text.length;
@@ -193,71 +174,52 @@ class RenameController extends Disposable implements IEditorContribution {
selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn;
}
return this.renameInputField.getInput(loc.range, loc.text, selectionStart, selectionEnd).then(newNameOrFocusFlag => {
const newNameOrFocusFlag = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd);
if (typeof newNameOrFocusFlag === 'boolean') {
if (newNameOrFocusFlag) {
this.editor.focus();
}
return undefined;
if (typeof newNameOrFocusFlag === 'boolean') {
if (newNameOrFocusFlag) {
this.editor.focus();
}
return undefined;
}
this.editor.focus();
const renameOperation = raceCancellation(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], this._cts.token), this._cts.token).then(async renameResult => {
if (!renameResult || !this.editor.hasModel()) {
return;
}
this.editor.focus();
if (renameResult.rejectReason) {
this._notificationService.info(renameResult.rejectReason);
return;
}
const state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll);
const editResult = await this._bulkEditService.apply(renameResult, { editor: this.editor });
const renameOperation = Promise.resolve(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], token).then(result => {
if (!this.editor.hasModel()) {
return undefined;
}
if (result.rejectReason) {
if (state.validate(this.editor)) {
MessageController.get(this.editor).showMessage(result.rejectReason, this.editor.getPosition());
} else {
this._notificationService.info(result.rejectReason);
}
return undefined;
}
return this._bulkEditService.apply(result, { editor: this.editor }).then(result => {
// alert
if (result.ariaSummary) {
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, result.ariaSummary));
}
});
}, err => {
this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute."));
return Promise.reject(err);
}));
this._progressService.showWhile(renameOperation, 250);
return renameOperation;
// alert
if (editResult.ariaSummary) {
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, editResult.ariaSummary));
}
}, err => {
this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute."));
return Promise.reject(err);
});
this._progressService.showWhile(renameOperation, 250);
return renameOperation;
}
public acceptRenameInput(): void {
if (this._renameInputField) {
this._renameInputField.acceptInput();
}
acceptRenameInput(): void {
this._renameInputField.getValue().acceptInput();
}
public cancelRenameInput(): void {
if (this._renameInputField) {
this._renameInputField.cancelInput(true);
}
}
private onModelChanged(): void {
if (this._activeRename) {
this._activeRename.operation.cancel();
this._activeRename = undefined;
this.cancelRenameInput();
}
cancelRenameInput(): void {
this._renameInputField.getValue().cancelInput(true);
}
}
@@ -312,7 +274,7 @@ export class RenameAction extends EditorAction {
}
}
registerEditorContribution(RenameController);
registerEditorContribution(RenameController.ID, RenameController);
registerEditorAction(RenameAction);
const RenameCommand = EditorCommand.bindToContribution<RenameController>(RenameController.get);

View File

@@ -89,14 +89,14 @@ export class RenameInputField implements IContentWidget, IDisposable {
const widgetShadowColor = theme.getColor(widgetShadow);
const border = theme.getColor(inputBorder);
this._inputField.style.backgroundColor = background ? background.toString() : null;
this._inputField.style.backgroundColor = background ? background.toString() : '';
this._inputField.style.color = foreground ? foreground.toString() : null;
this._inputField.style.borderWidth = border ? '1px' : '0px';
this._inputField.style.borderStyle = border ? 'solid' : 'none';
this._inputField.style.borderColor = border ? border.toString() : 'none';
this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : null;
this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : '';
}
private updateFont(): void {

View File

@@ -51,7 +51,7 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
setTimeout(() => BracketSelectionRangeProvider._bracketsRightYield(resolve, round + 1, model, pos, ranges));
break;
}
const key = bracket.close;
const key = bracket.close[0];
if (bracket.isOpen) {
// wait for closing
let val = counts.has(key) ? counts.get(key)! : 0;
@@ -96,7 +96,7 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
setTimeout(() => BracketSelectionRangeProvider._bracketsLeftYield(resolve, round + 1, model, pos, ranges, bucket));
break;
}
const key = bracket.close;
const key = bracket.close[0];
if (!bracket.isOpen) {
// wait for opening
let val = counts.has(key) ? counts.get(key)! : 0;

View File

@@ -47,10 +47,10 @@ class SelectionRanges {
class SmartSelectController implements IEditorContribution {
private static readonly _id = 'editor.contrib.smartSelectController';
public static readonly ID = 'editor.contrib.smartSelectController';
static get(editor: ICodeEditor): SmartSelectController {
return editor.getContribution<SmartSelectController>(SmartSelectController._id);
return editor.getContribution<SmartSelectController>(SmartSelectController.ID);
}
private readonly _editor: ICodeEditor;
@@ -67,10 +67,6 @@ class SmartSelectController implements IEditorContribution {
dispose(this._selectionListener);
}
getId(): string {
return SmartSelectController._id;
}
run(forward: boolean): Promise<void> | void {
if (!this._editor.hasModel()) {
return;
@@ -210,7 +206,7 @@ class ShrinkSelectionAction extends AbstractSmartSelect {
}
}
registerEditorContribution(SmartSelectController);
registerEditorContribution(SmartSelectController.ID, SmartSelectController);
registerEditorAction(GrowSelectionAction);
registerEditorAction(ShrinkSelectionAction);

View File

@@ -12,33 +12,11 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections';
import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect';
import { CancellationToken } from 'vs/base/common/cancellation';
import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections';
class TestTextResourcePropertiesService implements ITextResourcePropertiesService {
_serviceBrand: undefined;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
}
getEOL(resource: URI | undefined): string {
const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files');
if (filesConfiguration && filesConfiguration.eol) {
if (filesConfiguration.eol !== 'auto') {
return filesConfiguration.eol;
}
}
return (isLinux || isMacintosh) ? '\n' : '\r\n';
}
}
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test';
class MockJSMode extends MockMode {

View File

@@ -40,13 +40,15 @@ const _defaultOptions: ISnippetInsertOptions = {
export class SnippetController2 implements IEditorContribution {
public static ID = 'snippetController2';
static get(editor: ICodeEditor): SnippetController2 {
return editor.getContribution<SnippetController2>('snippetController2');
return editor.getContribution<SnippetController2>(SnippetController2.ID);
}
static InSnippetMode = new RawContextKey('inSnippetMode', false);
static HasNextTabstop = new RawContextKey('hasNextTabstop', false);
static HasPrevTabstop = new RawContextKey('hasPrevTabstop', false);
static readonly InSnippetMode = new RawContextKey('inSnippetMode', false);
static readonly HasNextTabstop = new RawContextKey('hasNextTabstop', false);
static readonly HasPrevTabstop = new RawContextKey('hasPrevTabstop', false);
private readonly _inSnippet: IContextKey<boolean>;
private readonly _hasNextTabstop: IContextKey<boolean>;
@@ -75,10 +77,6 @@ export class SnippetController2 implements IEditorContribution {
this._snippetListener.dispose();
}
getId(): string {
return 'snippetController2';
}
insert(
template: string,
opts?: Partial<ISnippetInsertOptions>
@@ -249,7 +247,7 @@ export class SnippetController2 implements IEditorContribution {
}
registerEditorContribution(SnippetController2);
registerEditorContribution(SnippetController2.ID, SnippetController2);
const CommandCtor = EditorCommand.bindToContribution<SnippetController2>(SnippetController2.get);

View File

@@ -664,27 +664,23 @@ export class SnippetParser {
}
private _until(type: TokenType): false | string {
if (this._token.type === TokenType.EOF) {
return false;
}
let res = '';
let pos = this._token.pos;
let prevToken = <Token>{ type: TokenType.EOF, pos: 0, len: 0 };
while (this._token.type !== type || prevToken.type === TokenType.Backslash) {
if (this._token.type === type) {
res += this._scanner.value.substring(pos, prevToken.pos);
pos = this._token.pos;
}
prevToken = this._token;
this._token = this._scanner.next();
const start = this._token;
while (this._token.type !== type) {
if (this._token.type === TokenType.EOF) {
return false;
} else if (this._token.type === TokenType.Backslash) {
const nextToken = this._scanner.next();
if (nextToken.type !== TokenType.Dollar
&& nextToken.type !== TokenType.CurlyClose
&& nextToken.type !== TokenType.Backslash) {
return false;
}
}
this._token = this._scanner.next();
}
res += this._scanner.value.substring(pos, this._token.pos);
const value = this._scanner.value.substring(start.pos, this._token.pos).replace(/\\(\$|}|\\)/g, '$1');
this._token = this._scanner.next();
return res;
return value;
}
private _parse(marker: Marker): boolean {

View File

@@ -18,7 +18,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet, Marker } from './snippetParser';
import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver } from './snippetVariables';
import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver, RandomBasedVariableResolver } from './snippetVariables';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import { withNullAsUndefined } from 'vs/base/common/types';
@@ -392,7 +392,7 @@ export class SnippetSession {
const modelBasedVariableResolver = editor.invokeWithinContext(accessor => new ModelBasedVariableResolver(accessor.get(ILabelService, optional), model));
const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional));
clipboardText = clipboardText || clipboardService && clipboardService.readTextSync();
const readClipboardText = () => clipboardText || clipboardService && clipboardService.readTextSync();
let delta = 0;
@@ -445,11 +445,12 @@ export class SnippetSession {
snippet.resolveVariables(new CompositeSnippetVariableResolver([
modelBasedVariableResolver,
new ClipboardBasedVariableResolver(clipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'),
new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'),
new SelectionBasedVariableResolver(model, selection),
new CommentBasedVariableResolver(model),
new TimeBasedVariableResolver,
new WorkspaceBasedVariableResolver(workspaceService),
new RandomBasedVariableResolver,
]));
const offset = model.getOffsetAt(start) + delta;

View File

@@ -12,8 +12,10 @@ import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snip
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ILabelService } from 'vs/platform/label/common/label';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { URI } from 'vs/base/common/uri';
export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({
'CURRENT_YEAR': true,
@@ -43,6 +45,9 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze(
'BLOCK_COMMENT_END': true,
'LINE_COMMENT': true,
'WORKSPACE_NAME': true,
'WORKSPACE_FOLDER': true,
'RANDOM': true,
'RANDOM_HEX': true,
});
export class CompositeSnippetVariableResolver implements VariableResolver {
@@ -164,10 +169,14 @@ export class ModelBasedVariableResolver implements VariableResolver {
}
}
export interface IReadClipboardText {
(): string | undefined;
}
export class ClipboardBasedVariableResolver implements VariableResolver {
constructor(
private readonly _clipboardText: string | undefined,
private readonly _readClipboardText: IReadClipboardText,
private readonly _selectionIdx: number,
private readonly _selectionCount: number,
private readonly _spread: boolean
@@ -180,7 +189,8 @@ export class ClipboardBasedVariableResolver implements VariableResolver {
return undefined;
}
if (!this._clipboardText) {
const clipboardText = this._readClipboardText();
if (!clipboardText) {
return undefined;
}
@@ -188,12 +198,12 @@ export class ClipboardBasedVariableResolver implements VariableResolver {
// text whenever there the line count equals the cursor count
// and when enabled
if (this._spread) {
const lines = this._clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s));
const lines = clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s));
if (lines.length === this._selectionCount) {
return lines[this._selectionIdx];
}
}
return this._clipboardText;
return clipboardText;
}
}
export class CommentBasedVariableResolver implements VariableResolver {
@@ -267,7 +277,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
}
resolve(variable: Variable): string | undefined {
if (variable.name !== 'WORKSPACE_NAME' || !this._workspaceService) {
if (!this._workspaceService) {
return undefined;
}
@@ -276,6 +286,15 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
return undefined;
}
if (variable.name === 'WORKSPACE_NAME') {
return this._resolveWorkspaceName(workspaceIdentifier);
} else if (variable.name === 'WORKSPACE_FOLDER') {
return this._resoveWorkspacePath(workspaceIdentifier);
}
return undefined;
}
private _resolveWorkspaceName(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return path.basename(workspaceIdentifier.path);
}
@@ -286,4 +305,31 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
}
return filename;
}
private _resoveWorkspacePath(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return normalizeDriveLetter(workspaceIdentifier.fsPath);
}
let filename = path.basename(workspaceIdentifier.configPath.path);
let folderpath = workspaceIdentifier.configPath.fsPath;
if (endsWith(folderpath, filename)) {
folderpath = folderpath.substr(0, folderpath.length - filename.length - 1);
}
return (folderpath ? normalizeDriveLetter(folderpath) : '/');
}
}
export class RandomBasedVariableResolver implements VariableResolver {
resolve(variable: Variable): string | undefined {
const { name } = variable;
if (name === 'RANDOM') {
return Math.random().toString().slice(-6);
}
else if (name === 'RANDOM_HEX') {
return Math.random().toString(16).slice(-6);
}
return undefined;
}
}

View File

@@ -45,7 +45,7 @@ suite('SnippetController', () => {
editor.getModel()!.updateOptions({
insertSpaces: false
});
let snippetController = editor.registerAndInstantiateContribution<TestSnippetController>(TestSnippetController);
let snippetController = editor.registerAndInstantiateContribution<TestSnippetController>(TestSnippetController.ID, TestSnippetController);
let template = [
'for (var ${1:index}; $1 < ${2:array}.length; $1++) {',
'\tvar element = $2[$1];',

View File

@@ -767,4 +767,17 @@ suite('SnippetParser', () => {
assert.equal((<FormatString>variable.transform!.children[0]).ifValue, 'import { hello } from world');
assert.equal((<FormatString>variable.transform!.children[0]).elseValue, undefined);
});
test('Snippet escape backslashes inside conditional insertion variable replacement #80394', function () {
let snippet = new SnippetParser().parse('${CURRENT_YEAR/(.+)/${1:+\\\\}/}');
let variable = <Variable>snippet.children[0];
assert.equal(snippet.children.length, 1);
assert.ok(variable instanceof Variable);
assert.ok(variable.transform);
assert.equal(variable.transform!.children.length, 1);
assert.ok(variable.transform!.children[0] instanceof FormatString);
assert.equal((<FormatString>variable.transform!.children[0]).ifValue, '\\');
assert.equal((<FormatString>variable.transform!.children[0]).elseValue, undefined);
});
});

View File

@@ -236,28 +236,28 @@ suite('Snippet Variables Resolver', function () {
test('Add variable to insert value from clipboard to a snippet #40153', function () {
assertVariableResolve(new ClipboardBasedVariableResolver(undefined, 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(() => undefined, 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(null!, 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(() => null!, 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver('', 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(() => '', 1, 0, true), 'CLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'CLIPBOARD', 'foo');
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'CLIPBOARD', 'foo');
assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'foo', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'cLIPBOARD', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'foo', undefined);
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'cLIPBOARD', undefined);
});
test('Add variable to insert value from clipboard to a snippet #40153', function () {
assertVariableResolve(new ClipboardBasedVariableResolver('line1', 1, 2, true), 'CLIPBOARD', 'line1');
assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3');
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1', 1, 2, true), 'CLIPBOARD', 'line1');
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3');
assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 1, 2, true), 'CLIPBOARD', 'line2');
resolver = new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true);
assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true), 'CLIPBOARD', 'line1');
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 1, 2, true), 'CLIPBOARD', 'line2');
resolver = new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, true);
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, true), 'CLIPBOARD', 'line1');
assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, false), 'CLIPBOARD', 'line1\nline2');
assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, false), 'CLIPBOARD', 'line1\nline2');
});
@@ -296,7 +296,7 @@ suite('Snippet Variables Resolver', function () {
assert.equal(snippet.toString(), 'It is not line 10');
});
test('Add workspace name variable for snippets #68261', function () {
test('Add workspace name and folder variables for snippets #68261', function () {
let workspace: IWorkspace;
let resolver: VariableResolver;
@@ -319,14 +319,21 @@ suite('Snippet Variables Resolver', function () {
// empty workspace
workspace = new Workspace('');
assertVariableResolve(resolver, 'WORKSPACE_NAME', undefined);
assertVariableResolve(resolver, 'WORKSPACE_FOLDER', undefined);
// single folder workspace without config
workspace = new Workspace('', [toWorkspaceFolder(URI.file('/folderName'))]);
assertVariableResolve(resolver, 'WORKSPACE_NAME', 'folderName');
if (!isWindows) {
assertVariableResolve(resolver, 'WORKSPACE_FOLDER', '/folderName');
}
// workspace with config
const workspaceConfigPath = URI.file('testWorkspace.code-workspace');
workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }], workspaceConfigPath), workspaceConfigPath);
assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace');
if (!isWindows) {
assertVariableResolve(resolver, 'WORKSPACE_FOLDER', '/');
}
});
});

View File

@@ -11,7 +11,7 @@ import { ISelectedSuggestion } from './suggestWidget';
export class SuggestAlternatives {
static OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);
static readonly OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);
private readonly _ckOtherSuggestions: IContextKey<boolean>;

View File

@@ -19,10 +19,10 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Context as SuggestContext, CompletionItem } from './suggest';
import { SuggestAlternatives } from './suggestAlternatives';
import { State, SuggestModel } from './suggestModel';
@@ -86,9 +86,16 @@ class LineSuffix {
}
}
const enum InsertFlags {
NoBeforeUndoStop = 1,
NoAfterUndoStop = 2,
KeepAlternativeSuggestions = 4,
AlternativeOverwriteConfig = 8
}
export class SuggestController implements IEditorContribution {
private static readonly ID: string = 'editor.contrib.suggestController';
public static readonly ID: string = 'editor.contrib.suggestController';
public static get(editor: ICodeEditor): SuggestController {
return editor.getContribution<SuggestController>(SuggestController.ID);
@@ -115,10 +122,10 @@ export class SuggestController implements IEditorContribution {
const widget = this._instantiationService.createInstance(SuggestWidget, this._editor);
this._toDispose.add(widget);
this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true), this));
this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, 0), this));
// Wire up logic to accept a suggestion on certain characters
const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true));
const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop));
this._toDispose.add(commitCharacterController);
this._toDispose.add(this._model.onDidSuggest(e => {
if (e.completionModel.items.length === 0) {
@@ -210,11 +217,6 @@ export class SuggestController implements IEditorContribution {
updateFromConfig();
}
getId(): string {
return SuggestController.ID;
}
dispose(): void {
this._alternatives.dispose();
this._toDispose.dispose();
@@ -223,7 +225,10 @@ export class SuggestController implements IEditorContribution {
this._lineSuffix.dispose();
}
protected _insertSuggestion(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void {
protected _insertSuggestion(
event: ISelectedSuggestion | undefined,
flags: InsertFlags
): void {
if (!event || !event.item) {
this._alternatives.getValue().reset();
this._model.cancel();
@@ -237,12 +242,11 @@ export class SuggestController implements IEditorContribution {
const model = this._editor.getModel();
const modelVersionNow = model.getAlternativeVersionId();
const { completion: suggestion, position } = event.item;
const editorColumn = this._editor.getPosition().column;
const columnDelta = editorColumn - position.column;
const columnDelta = this._editor.getPosition().column - position.column;
// pushing undo stops *before* additional text edits and
// *after* the main edit
if (undoStops) {
if (!(flags & InsertFlags.NoBeforeUndoStop)) {
this._editor.pushUndoStop();
}
@@ -258,9 +262,26 @@ export class SuggestController implements IEditorContribution {
insertText = SnippetParser.escape(insertText);
}
const overwriteBefore = position.column - suggestion.range.startColumn;
const overwriteAfter = suggestion.range.endColumn - position.column;
const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0;
let overwriteBefore = position.column - suggestion.range.startColumn;
let overwriteAfter = suggestion.range.endColumn - position.column;
let suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0;
let word = model.getWordAtPosition(this._editor.getPosition());
const overwriteConfig = flags & InsertFlags.AlternativeOverwriteConfig
? !this._editor.getOption(EditorOption.suggest).overwriteOnAccept
: this._editor.getOption(EditorOption.suggest).overwriteOnAccept;
if (!overwriteConfig) {
if (overwriteAfter > 0 && word && suggestion.range.endColumn === word.endColumn) {
// don't overwrite anything right of the cursor, overrule extension even when the
// completion only replaces a word...
overwriteAfter = 0;
}
} else {
if (overwriteAfter === 0 && word) {
// compute fallback overwrite length
overwriteAfter = word.endColumn - this._editor.getPosition().column;
}
}
SnippetController2.get(this._editor).insert(insertText, {
overwriteBefore: overwriteBefore + columnDelta,
@@ -270,7 +291,7 @@ export class SuggestController implements IEditorContribution {
adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace)
});
if (undoStops) {
if (!(flags & InsertFlags.NoAfterUndoStop)) {
this._editor.pushUndoStop();
}
@@ -291,7 +312,7 @@ export class SuggestController implements IEditorContribution {
this._model.cancel();
}
if (keepAlternativeSuggestions) {
if (flags & InsertFlags.KeepAlternativeSuggestions) {
this._alternatives.getValue().set(event, next => {
// this is not so pretty. when inserting the 'next'
// suggestion we undo until we are at the state at
@@ -300,7 +321,10 @@ export class SuggestController implements IEditorContribution {
if (modelVersionNow !== model.getAlternativeVersionId()) {
model.undo();
}
this._insertSuggestion(next, false, false);
this._insertSuggestion(
next,
InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop | (flags & InsertFlags.AlternativeOverwriteConfig ? InsertFlags.AlternativeOverwriteConfig : 0)
);
break;
}
});
@@ -382,7 +406,7 @@ export class SuggestController implements IEditorContribution {
return;
}
this._editor.pushUndoStop();
this._insertSuggestion({ index, item, model: completionModel }, true, false);
this._insertSuggestion({ index, item, model: completionModel }, InsertFlags.KeepAlternativeSuggestions | InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop);
}, undefined, listener);
});
@@ -392,9 +416,16 @@ export class SuggestController implements IEditorContribution {
this._editor.focus();
}
acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void {
acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, alternativeOverwriteConfig: boolean): void {
const item = this._widget.getValue().getFocusedItem();
this._insertSuggestion(item, !!keepAlternativeSuggestions, true);
let flags = 0;
if (keepAlternativeSuggestions) {
flags |= InsertFlags.KeepAlternativeSuggestions;
}
if (alternativeOverwriteConfig) {
flags |= InsertFlags.AlternativeOverwriteConfig;
}
this._insertSuggestion(item, flags);
}
acceptNextSuggestion() {
@@ -478,7 +509,7 @@ export class TriggerSuggestAction extends EditorAction {
}
}
registerEditorContribution(SuggestController);
registerEditorContribution(SuggestController.ID, SuggestController);
registerEditorAction(TriggerSuggestAction);
const weight = KeybindingWeight.EditorContrib + 90;
@@ -489,24 +520,43 @@ const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(Sugge
registerEditorCommand(new SuggestCommand({
id: 'acceptSelectedSuggestion',
precondition: SuggestContext.Visible,
handler: x => x.acceptSelectedSuggestion(true),
kbOpts: {
weight: weight,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Tab
handler(x, args) {
const alternative: boolean = typeof args === 'object' && typeof args.alternative === 'boolean'
? args.alternative
: false;
x.acceptSelectedSuggestion(true, alternative);
}
}));
registerEditorCommand(new SuggestCommand({
id: 'acceptSelectedSuggestionOnEnter',
precondition: SuggestContext.Visible,
handler: x => x.acceptSelectedSuggestion(false),
kbOpts: {
weight: weight,
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
primary: KeyCode.Enter
}
}));
// normal tab
KeybindingsRegistry.registerKeybindingRule({
id: 'acceptSelectedSuggestion',
when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
primary: KeyCode.Tab,
weight
});
// accept on enter has special rules
KeybindingsRegistry.registerKeybindingRule({
id: 'acceptSelectedSuggestion',
when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
primary: KeyCode.Enter,
weight
});
// shift+enter and shift+tab use the alternative-flag so that the suggest controller
// is doing the opposite of the editor.suggest.overwriteOnAccept-configuration
KeybindingsRegistry.registerKeybindingRule({
id: 'acceptSelectedSuggestion',
when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
primary: KeyMod.Shift | KeyCode.Tab,
secondary: [KeyMod.Shift | KeyCode.Enter],
args: { alternative: true },
weight
});
// continue to support the old command
CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion');
registerEditorCommand(new SuggestCommand({
id: 'hideSuggestWidget',

View File

@@ -116,7 +116,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
const text = append(container, $('.contents'));
const main = append(text, $('.main'));
data.iconLabel = new IconLabel(main, { supportHighlights: true, supportOcticons: true });
data.iconLabel = new IconLabel(main, { supportHighlights: true, supportCodicons: true });
data.disposables.add(data.iconLabel);
data.typeLabel = append(main, $('span.type-label'));
@@ -174,10 +174,9 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) {
// special logic for 'file' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE)
]);
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE);
const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE);
labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getIconTheme().hasFolderIcons) {
// special logic for 'folder' completion items

View File

@@ -159,6 +159,7 @@ suite('CompletionModel', function () {
leadingLineContent: 's',
characterCountDelta: 0
}, WordDistance.None, {
overwriteOnAccept: false,
snippetsPreventQuickSuggestions: true,
filterGraceful: true,
localityBonus: false,
@@ -186,6 +187,7 @@ suite('CompletionModel', function () {
leadingLineContent: 's',
characterCountDelta: 0
}, WordDistance.None, {
overwriteOnAccept: false,
snippetsPreventQuickSuggestions: true,
filterGraceful: true,
localityBonus: false,
@@ -212,6 +214,7 @@ suite('CompletionModel', function () {
leadingLineContent: 's',
characterCountDelta: 0
}, WordDistance.None, {
overwriteOnAccept: false,
snippetsPreventQuickSuggestions: true,
filterGraceful: true,
localityBonus: false,

View File

@@ -59,7 +59,7 @@ function createMockEditor(model: TextModel): TestCodeEditor {
}],
),
});
editor.registerAndInstantiateContribution(SnippetController2);
editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2);
return editor;
}
@@ -675,12 +675,12 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
return withOracle(async (sugget, editor) => {
class TestCtrl extends SuggestController {
_insertSuggestion(item: ISelectedSuggestion) {
super._insertSuggestion(item, false, true);
_insertSuggestion(item: ISelectedSuggestion, flags: number = 0) {
super._insertSuggestion(item, flags);
}
}
const ctrl = <TestCtrl>editor.registerAndInstantiateContribution(TestCtrl);
editor.registerAndInstantiateContribution(SnippetController2);
const ctrl = <TestCtrl>editor.registerAndInstantiateContribution(TestCtrl.ID, TestCtrl);
editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2);
await assertEvent(sugget.onDidSuggest, () => {
editor.setPosition({ lineNumber: 1, column: 3 });

View File

@@ -458,7 +458,7 @@ class WordHighlighter {
class WordHighlighterContribution extends Disposable implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.wordHighlighter';
public static readonly ID = 'editor.contrib.wordHighlighter';
public static get(editor: ICodeEditor): WordHighlighterContribution {
return editor.getContribution<WordHighlighterContribution>(WordHighlighterContribution.ID);
@@ -484,10 +484,6 @@ class WordHighlighterContribution extends Disposable implements editorCommon.IEd
createWordHighlighterIfPossible();
}
public getId(): string {
return WordHighlighterContribution.ID;
}
public saveViewState(): boolean {
if (this.wordHighligher && this.wordHighligher.hasDecorations()) {
return true;
@@ -603,7 +599,7 @@ class TriggerWordHighlightAction extends EditorAction {
}
}
registerEditorContribution(WordHighlighterContribution);
registerEditorContribution(WordHighlighterContribution.ID, WordHighlighterContribution);
registerEditorAction(NextWordHighlightAction);
registerEditorAction(PrevWordHighlightAction);
registerEditorAction(TriggerWordHighlightAction);

View File

@@ -50,11 +50,11 @@ const WIDGET_ID = 'vs.editor.contrib.zoneWidget';
export class ViewZoneDelegate implements IViewZone {
public domNode: HTMLElement;
public id: string = ''; // A valid zone id should be greater than 0
public afterLineNumber: number;
public afterColumn: number;
public heightInLines: number;
domNode: HTMLElement;
id: string = ''; // A valid zone id should be greater than 0
afterLineNumber: number;
afterColumn: number;
heightInLines: number;
private readonly _onDomNodeTop: (top: number) => void;
private readonly _onComputedHeight: (height: number) => void;
@@ -71,11 +71,11 @@ export class ViewZoneDelegate implements IViewZone {
this._onComputedHeight = onComputedHeight;
}
public onDomNodeTop(top: number): void {
onDomNodeTop(top: number): void {
this._onDomNodeTop(top);
}
public onComputedHeight(height: number): void {
onComputedHeight(height: number): void {
this._onComputedHeight(height);
}
}
@@ -90,15 +90,15 @@ export class OverlayWidgetDelegate implements IOverlayWidget {
this._domNode = domNode;
}
public getId(): string {
getId(): string {
return this._id;
}
public getDomNode(): HTMLElement {
getDomNode(): HTMLElement {
return this._domNode;
}
public getPosition(): IOverlayWidgetPosition | null {
getPosition(): IOverlayWidgetPosition | null {
return null;
}
}
@@ -167,10 +167,10 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
protected _viewZone: ViewZoneDelegate | null = null;
protected readonly _disposables = new DisposableStore();
public container: HTMLElement | null = null;
public domNode: HTMLElement;
public editor: ICodeEditor;
public options: IOptions;
container: HTMLElement | null = null;
domNode: HTMLElement;
editor: ICodeEditor;
options: IOptions;
constructor(editor: ICodeEditor, options: IOptions = {}) {
@@ -191,7 +191,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
}));
}
public dispose(): void {
dispose(): void {
if (this._overlayWidget) {
this.editor.removeOverlayWidget(this._overlayWidget);
this._overlayWidget = null;
@@ -212,7 +212,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
this._disposables.dispose();
}
public create(): void {
create(): void {
dom.addClass(this.domNode, 'zone-widget');
if (this.options.className) {
@@ -231,7 +231,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
this._applyStyles();
}
public style(styles: IStyles): void {
style(styles: IStyles): void {
if (styles.frameColor) {
this.options.frameColor = styles.frameColor;
}
@@ -284,7 +284,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
}
}
public get position(): Position | undefined {
get position(): Position | undefined {
const [id] = this._positionMarkerId;
if (!id) {
return undefined;
@@ -304,18 +304,15 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
protected _isShowing: boolean = false;
public show(rangeOrPos: IRange | IPosition, heightInLines: number): void {
const range = Range.isIRange(rangeOrPos)
? rangeOrPos
: new Range(rangeOrPos.lineNumber, rangeOrPos.column, rangeOrPos.lineNumber, rangeOrPos.column);
show(rangeOrPos: IRange | IPosition, heightInLines: number): void {
const range = Range.isIRange(rangeOrPos) ? Range.lift(rangeOrPos) : Range.fromPositions(rangeOrPos);
this._isShowing = true;
this._showImpl(range, heightInLines);
this._isShowing = false;
this._positionMarkerId = this.editor.deltaDecorations(this._positionMarkerId, [{ range, options: ModelDecorationOptions.EMPTY }]);
}
public hide(): void {
hide(): void {
if (this._viewZone) {
this.editor.changeViewZones(accessor => {
if (this._viewZone) {
@@ -350,12 +347,8 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
return result;
}
private _showImpl(where: IRange, heightInLines: number): void {
const position = {
lineNumber: where.startLineNumber,
column: where.startColumn
};
private _showImpl(where: Range, heightInLines: number): void {
const position = where.getStartPosition();
const layoutInfo = this.editor.getLayoutInfo();
const width = this._getWidth(layoutInfo);
this.domNode.style.width = `${width}px`;
@@ -432,14 +425,23 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider {
const model = this.editor.getModel();
if (model) {
// Reveal the line above or below the zone widget, to get the zone widget in the viewport
const revealLineNumber = Math.min(model.getLineCount(), Math.max(1, where.endLineNumber + 1));
this.revealLine(revealLineNumber);
const revealLine = where.endLineNumber + 1;
if (revealLine <= model.getLineCount()) {
// reveal line below the zone widget
this.revealLine(revealLine, false);
} else {
// reveal last line atop
this.revealLine(model.getLineCount(), true);
}
}
}
protected revealLine(lineNumber: number) {
this.editor.revealLine(lineNumber, ScrollType.Smooth);
protected revealLine(lineNumber: number, isLastLine: boolean) {
if (isLastLine) {
this.editor.revealLineInCenter(lineNumber, ScrollType.Smooth);
} else {
this.editor.revealLine(lineNumber, ScrollType.Smooth);
}
}
protected setCssClass(className: string, classToReplace?: string): void {