mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-22 21:00:30 -04:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -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) => {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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); */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>`;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .outline-element-decoration.bubble {
|
||||
font-family: octicons;
|
||||
font-family: codicon;
|
||||
font-size: 14px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
@@ -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 }));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', '/');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user