mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-24 05:40:29 -04:00
Merge from vscode cfc1ab4c5f816765b91fb7ead3c3427a7c8581a3
This commit is contained in:
@@ -34,7 +34,7 @@ suite('bracket matching', () => {
|
||||
let model = createTextModel('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
// start on closing bracket
|
||||
editor.setPosition(new Position(1, 20));
|
||||
@@ -66,7 +66,7 @@ suite('bracket matching', () => {
|
||||
let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
// start position between brackets
|
||||
editor.setPosition(new Position(1, 16));
|
||||
@@ -103,7 +103,7 @@ suite('bracket matching', () => {
|
||||
let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
|
||||
// start position in open brackets
|
||||
@@ -155,7 +155,7 @@ suite('bracket matching', () => {
|
||||
const model = createTextModel(text, undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
editor.setPosition(new Position(3, 5));
|
||||
bracketMatchingController.jumpToBracket();
|
||||
@@ -180,7 +180,7 @@ suite('bracket matching', () => {
|
||||
const model = createTextModel(text, undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
const bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
editor.setPosition(new Position(3, 5));
|
||||
bracketMatchingController.selectToBracket(false);
|
||||
@@ -198,7 +198,7 @@ suite('bracket matching', () => {
|
||||
let model = createTextModel('{ } { } { }', undefined, mode.getLanguageIdentifier());
|
||||
|
||||
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController.ID, BracketMatchingController);
|
||||
let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController);
|
||||
|
||||
// cursors inside brackets become selections of the entire bracket contents
|
||||
editor.setSelections([
|
||||
|
||||
@@ -13,66 +13,43 @@ export class MoveCaretCommand implements ICommand {
|
||||
private readonly _selection: Selection;
|
||||
private readonly _isMovingLeft: boolean;
|
||||
|
||||
private _cutStartIndex: number;
|
||||
private _cutEndIndex: number;
|
||||
private _moved: boolean;
|
||||
|
||||
private _selectionId: string | null;
|
||||
|
||||
constructor(selection: Selection, isMovingLeft: boolean) {
|
||||
this._selection = selection;
|
||||
this._isMovingLeft = isMovingLeft;
|
||||
this._cutStartIndex = -1;
|
||||
this._cutEndIndex = -1;
|
||||
this._moved = false;
|
||||
this._selectionId = null;
|
||||
}
|
||||
|
||||
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
|
||||
let s = this._selection;
|
||||
this._selectionId = builder.trackSelection(s);
|
||||
if (s.startLineNumber !== s.endLineNumber) {
|
||||
if (this._selection.startLineNumber !== this._selection.endLineNumber || this._selection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (this._isMovingLeft && s.startColumn === 0) {
|
||||
return;
|
||||
} else if (!this._isMovingLeft && s.endColumn === model.getLineMaxColumn(s.startLineNumber)) {
|
||||
const lineNumber = this._selection.startLineNumber;
|
||||
const startColumn = this._selection.startColumn;
|
||||
const endColumn = this._selection.endColumn;
|
||||
if (this._isMovingLeft && startColumn === 1) {
|
||||
return;
|
||||
}
|
||||
if (!this._isMovingLeft && endColumn === model.getLineMaxColumn(lineNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lineNumber = s.selectionStartLineNumber;
|
||||
let lineContent = model.getLineContent(lineNumber);
|
||||
|
||||
let left: string;
|
||||
let middle: string;
|
||||
let right: string;
|
||||
|
||||
if (this._isMovingLeft) {
|
||||
left = lineContent.substring(0, s.startColumn - 2);
|
||||
middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1);
|
||||
right = lineContent.substring(s.startColumn - 2, s.startColumn - 1) + lineContent.substring(s.endColumn - 1);
|
||||
const rangeBefore = new Range(lineNumber, startColumn - 1, lineNumber, startColumn);
|
||||
const charBefore = model.getValueInRange(rangeBefore);
|
||||
builder.addEditOperation(rangeBefore, null);
|
||||
builder.addEditOperation(new Range(lineNumber, endColumn, lineNumber, endColumn), charBefore);
|
||||
} else {
|
||||
left = lineContent.substring(0, s.startColumn - 1) + lineContent.substring(s.endColumn - 1, s.endColumn);
|
||||
middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1);
|
||||
right = lineContent.substring(s.endColumn);
|
||||
const rangeAfter = new Range(lineNumber, endColumn, lineNumber, endColumn + 1);
|
||||
const charAfter = model.getValueInRange(rangeAfter);
|
||||
builder.addEditOperation(rangeAfter, null);
|
||||
builder.addEditOperation(new Range(lineNumber, startColumn, lineNumber, startColumn), charAfter);
|
||||
}
|
||||
|
||||
let newLineContent = left + middle + right;
|
||||
|
||||
builder.addEditOperation(new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber)), null);
|
||||
builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), newLineContent);
|
||||
|
||||
this._cutStartIndex = s.startColumn + (this._isMovingLeft ? -1 : 1);
|
||||
this._cutEndIndex = this._cutStartIndex + s.endColumn - s.startColumn;
|
||||
this._moved = true;
|
||||
}
|
||||
|
||||
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
|
||||
let result = helper.getTrackedSelection(this._selectionId!);
|
||||
if (this._moved) {
|
||||
result = result.setStartPosition(result.startLineNumber, this._cutStartIndex);
|
||||
result = result.setEndPosition(result.startLineNumber, this._cutEndIndex);
|
||||
if (this._isMovingLeft) {
|
||||
return new Selection(this._selection.startLineNumber, this._selection.startColumn - 1, this._selection.endLineNumber, this._selection.endColumn - 1);
|
||||
} else {
|
||||
return new Selection(this._selection.startLineNumber, this._selection.startColumn + 1, this._selection.endLineNumber, this._selection.endColumn + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
|
||||
const supportsCut = (platform.isNative || document.queryCommandSupported('cut'));
|
||||
const supportsCopy = (platform.isNative || document.queryCommandSupported('copy'));
|
||||
// IE and Edge have trouble with setting html content in clipboard
|
||||
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE);
|
||||
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge);
|
||||
// Chrome incorrectly returns true for document.queryCommandSupported('paste')
|
||||
// when the paste feature is available but the calling script has insufficient
|
||||
// privileges to actually perform the action
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -123,7 +122,7 @@ export class CodeActionUi extends Disposable {
|
||||
if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length === 0)
|
||||
|| (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.allActions.length === 1)
|
||||
) {
|
||||
return find(actions.allActions, action => action.disabled);
|
||||
return actions.allActions.find(action => action.disabled);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Gesture } from 'vs/base/browser/touch';
|
||||
import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
|
||||
@@ -222,7 +222,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Lightbulb Icon
|
||||
const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens';
|
||||
import { LRUCache, values } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes';
|
||||
import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -103,7 +103,7 @@ export class CodeLensCache implements ICodeLensCache {
|
||||
}
|
||||
data[key] = {
|
||||
lineCount: value.lineCount,
|
||||
lines: values(lines)
|
||||
lines: [...lines.values()]
|
||||
};
|
||||
});
|
||||
return JSON.stringify(data);
|
||||
|
||||
@@ -8,10 +8,10 @@ import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/err
|
||||
import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
|
||||
import { ICodeEditor, MouseTargetType, IViewZoneChangeAccessor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { registerEditorContribution, ServicesAccessor, registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
import { CodeLensProviderRegistry, CodeLens } from 'vs/editor/common/modes';
|
||||
import { CodeLensProviderRegistry, CodeLens, Command } from 'vs/editor/common/modes';
|
||||
import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens';
|
||||
import { CodeLensWidget, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -20,6 +20,9 @@ import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { localize } from 'vs/nls';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
|
||||
export class CodeLensContribution implements IEditorContribution {
|
||||
|
||||
@@ -402,6 +405,70 @@ export class CodeLensContribution implements IEditorContribution {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getLenses(): readonly CodeLensWidget[] {
|
||||
return this._lenses;
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(CodeLensContribution.ID, CodeLensContribution);
|
||||
|
||||
registerEditorAction(class ShowLensesInCurrentLine extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'codelens.showLensesInCurrentLine',
|
||||
precondition: EditorContextKeys.hasCodeLensProvider,
|
||||
label: localize('showLensOnLine', "Show Code Lens Command For Current Line"),
|
||||
alias: 'Show Code Lens Commands For Current Line',
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
|
||||
const lineNumber = editor.getSelection().positionLineNumber;
|
||||
const codelensController = editor.getContribution<CodeLensContribution>(CodeLensContribution.ID);
|
||||
const items: { label: string, command: Command }[] = [];
|
||||
|
||||
for (let lens of codelensController.getLenses()) {
|
||||
if (lens.getLineNumber() === lineNumber) {
|
||||
for (let item of lens.getItems()) {
|
||||
const { command } = item.symbol;
|
||||
if (command) {
|
||||
items.push({
|
||||
label: command.title,
|
||||
command: command
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
// We dont want an empty picker
|
||||
return;
|
||||
}
|
||||
|
||||
const item = await quickInputService.pick(items, { canPickMany: false });
|
||||
if (!item) {
|
||||
// Nothing picked
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await commandService.executeCommand(item.command.id, ...(item.command.arguments || []));
|
||||
} catch (err) {
|
||||
notificationService.error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -336,6 +336,10 @@ export class CodeLensWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getItems(): CodeLensItem[] {
|
||||
return this._data;
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ColorPickerHeader extends Disposable {
|
||||
const colorBox = dom.append(this.domNode, $('.original-color'));
|
||||
colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || '';
|
||||
|
||||
this.backgroundColor = themeService.getTheme().getColor(editorHoverBackground) || Color.white;
|
||||
this.backgroundColor = themeService.getColorTheme().getColor(editorHoverBackground) || Color.white;
|
||||
this._register(registerThemingParticipant((theme, collector) => {
|
||||
this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white;
|
||||
}));
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline';
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, listErrorForeground, listWarningForeground, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
@@ -150,7 +150,7 @@ export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, Fuz
|
||||
}
|
||||
|
||||
const { count, topSev } = element.marker;
|
||||
const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
|
||||
const color = this._themeService.getColorTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
|
||||
const cssColor = color ? color.toString() : 'inherit';
|
||||
|
||||
// color of the label
|
||||
@@ -536,7 +536,7 @@ export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variabl
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.variableForeground', 'The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
||||
const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND);
|
||||
if (symbolIconArrayColor) {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function getSelectionSearchString(editor: ICodeEditor): string | null {
|
||||
// if selection spans multiple lines, default search string to empty
|
||||
if (selection.startLineNumber === selection.endLineNumber) {
|
||||
if (selection.isEmpty()) {
|
||||
let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition());
|
||||
const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition());
|
||||
if (wordAtPosition) {
|
||||
return wordAtPosition.word;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import { FindDecorations } from 'vs/editor/contrib/find/findDecorations';
|
||||
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
|
||||
import { ReplaceAllCommand } from 'vs/editor/contrib/find/replaceAllCommand';
|
||||
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
|
||||
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('findWidgetVisible', false);
|
||||
export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated();
|
||||
export const CONTEXT_FIND_WIDGET_NOT_VISIBLE = CONTEXT_FIND_WIDGET_VISIBLE.toNegated();
|
||||
// Keep ContextKey use of 'Focussed' to not break when clauses
|
||||
export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey<boolean>('findInputFocussed', false);
|
||||
export const CONTEXT_REPLACE_INPUT_FOCUSED = new RawContextKey<boolean>('replaceInputFocussed', false);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FIND_IDS } from 'vs/editor/contrib/find/findModel';
|
||||
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
@@ -46,8 +46,8 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this._domNode.setAttribute('role', 'presentation');
|
||||
this._domNode.setAttribute('aria-hidden', 'true');
|
||||
|
||||
const inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder);
|
||||
const inputActiveOptionBackgroundColor = themeService.getTheme().getColor(inputActiveOptionBackground);
|
||||
const inputActiveOptionBorderColor = themeService.getColorTheme().getColor(inputActiveOptionBorder);
|
||||
const inputActiveOptionBackgroundColor = themeService.getColorTheme().getColor(inputActiveOptionBackground);
|
||||
|
||||
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
|
||||
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand),
|
||||
@@ -112,8 +112,8 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this._register(dom.addDisposableNonBubblingMouseOutListener(this._domNode, (e) => this._onMouseOut()));
|
||||
this._register(dom.addDisposableListener(this._domNode, 'mouseover', (e) => this._onMouseOver()));
|
||||
|
||||
this._applyTheme(themeService.getTheme());
|
||||
this._register(themeService.onThemeChange(this._applyTheme.bind(this)));
|
||||
this._applyTheme(themeService.getColorTheme());
|
||||
this._register(themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
|
||||
}
|
||||
|
||||
private _keybindingLabelFor(actionId: string): string {
|
||||
@@ -182,7 +182,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this._domNode.style.display = 'none';
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
private _applyTheme(theme: IColorTheme) {
|
||||
let inputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground)
|
||||
|
||||
@@ -31,7 +31,7 @@ import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contri
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
@@ -244,8 +244,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line.
|
||||
}
|
||||
|
||||
this._applyTheme(themeService.getTheme());
|
||||
this._register(themeService.onThemeChange(this._applyTheme.bind(this)));
|
||||
this._applyTheme(themeService.getColorTheme());
|
||||
this._register(themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
|
||||
|
||||
this._register(this._codeEditor.onDidChangeModel(() => {
|
||||
if (!this._isVisible) {
|
||||
@@ -643,7 +643,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
});
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
private _applyTheme(theme: IColorTheme) {
|
||||
let inputStyles: IFindInputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground),
|
||||
|
||||
@@ -89,7 +89,7 @@ suite('FindController', () => {
|
||||
assert.ok(true);
|
||||
return;
|
||||
}
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let findState = findController.getState();
|
||||
let nextMatchFindAction = new NextMatchFindAction();
|
||||
|
||||
@@ -141,7 +141,7 @@ suite('FindController', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(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.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
findController.toggleRegex();
|
||||
assert.equal(queryState['editor.isRegex'], true);
|
||||
|
||||
@@ -522,7 +522,7 @@ suite('FindController query options persistence', () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 1, 2, 1));
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
findController.start({
|
||||
forceRevealReplace: false,
|
||||
@@ -545,7 +545,7 @@ suite('FindController query options persistence', () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 2, 1, 2));
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
findController.start({
|
||||
forceRevealReplace: false,
|
||||
@@ -568,7 +568,7 @@ suite('FindController query options persistence', () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 2, 1, 3));
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
findController.start({
|
||||
forceRevealReplace: false,
|
||||
@@ -592,7 +592,7 @@ suite('FindController query options persistence', () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, (editor, cursor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 6, 2, 1));
|
||||
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController.ID, TestFindController);
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
findController.start({
|
||||
forceRevealReplace: false,
|
||||
|
||||
@@ -13,7 +13,8 @@ export class FoldingDecorationProvider implements IDecorationProvider {
|
||||
private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
afterContentClassName: 'inline-folded',
|
||||
linesDecorationsClassName: 'codicon codicon-chevron-right'
|
||||
isWholeLine: true,
|
||||
firstLineDecorationClassName: 'codicon codicon-chevron-right'
|
||||
});
|
||||
|
||||
private static readonly COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION = ModelDecorationOptions.register({
|
||||
@@ -21,17 +22,23 @@ export class FoldingDecorationProvider implements IDecorationProvider {
|
||||
afterContentClassName: 'inline-folded',
|
||||
className: 'folded-background',
|
||||
isWholeLine: true,
|
||||
linesDecorationsClassName: 'codicon codicon-chevron-right'
|
||||
firstLineDecorationClassName: 'codicon codicon-chevron-right'
|
||||
});
|
||||
|
||||
private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
linesDecorationsClassName: 'codicon codicon-chevron-down'
|
||||
isWholeLine: true,
|
||||
firstLineDecorationClassName: 'codicon codicon-chevron-down'
|
||||
});
|
||||
|
||||
private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
linesDecorationsClassName: 'codicon codicon-chevron-down alwaysShowFoldIcons'
|
||||
isWholeLine: true,
|
||||
firstLineDecorationClassName: 'codicon codicon-chevron-down alwaysShowFoldIcons'
|
||||
});
|
||||
|
||||
private static readonly HIDDEN_RANGE_DECORATION = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
});
|
||||
|
||||
public autoHideFoldingControls: boolean = true;
|
||||
@@ -41,7 +48,10 @@ export class FoldingDecorationProvider implements IDecorationProvider {
|
||||
constructor(private readonly editor: ICodeEditor) {
|
||||
}
|
||||
|
||||
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
|
||||
getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions {
|
||||
if (isHidden) {
|
||||
return FoldingDecorationProvider.HIDDEN_RANGE_DECORATION;
|
||||
}
|
||||
if (isCollapsed) {
|
||||
return this.showFoldingHighlights ? FoldingDecorationProvider.COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION;
|
||||
} else if (this.autoHideFoldingControls) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges';
|
||||
|
||||
export interface IDecorationProvider {
|
||||
getDecorationOption(isCollapsed: boolean): IModelDecorationOptions;
|
||||
getDecorationOption(isCollapsed: boolean, isHidden: boolean): IModelDecorationOptions;
|
||||
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[];
|
||||
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null;
|
||||
}
|
||||
@@ -34,6 +34,7 @@ export class FoldingModel {
|
||||
public get regions(): FoldingRegions { return this._regions; }
|
||||
public get textModel() { return this._textModel; }
|
||||
public get isInitialized() { return this._isInitialized; }
|
||||
public get decorationProvider() { return this._decorationProvider; }
|
||||
|
||||
constructor(textModel: ITextModel, decorationProvider: IDecorationProvider) {
|
||||
this._textModel = textModel;
|
||||
@@ -43,24 +44,47 @@ export class FoldingModel {
|
||||
this._isInitialized = false;
|
||||
}
|
||||
|
||||
public toggleCollapseState(regions: FoldingRegion[]) {
|
||||
if (!regions.length) {
|
||||
public toggleCollapseState(toggledRegions: FoldingRegion[]) {
|
||||
if (!toggledRegions.length) {
|
||||
return;
|
||||
}
|
||||
let processed: { [key: string]: boolean | undefined } = {};
|
||||
toggledRegions = toggledRegions.sort((r1, r2) => r1.regionIndex - r2.regionIndex);
|
||||
|
||||
const processed: { [key: string]: boolean | undefined } = {};
|
||||
this._decorationProvider.changeDecorations(accessor => {
|
||||
for (let region of regions) {
|
||||
let k = 0; // index from [0 ... this.regions.length]
|
||||
let dirtyRegionEndLine = -1; // end of the range where decorations need to be updated
|
||||
let lastHiddenLine = -1; // the end of the last hidden lines
|
||||
const updateDecorationsUntil = (index: number) => {
|
||||
while (k < index) {
|
||||
const endLineNumber = this._regions.getEndLineNumber(k);
|
||||
const isCollapsed = this._regions.isCollapsed(k);
|
||||
if (endLineNumber <= dirtyRegionEndLine) {
|
||||
accessor.changeDecorationOptions(this._editorDecorationIds[k], this._decorationProvider.getDecorationOption(isCollapsed, endLineNumber <= lastHiddenLine));
|
||||
}
|
||||
if (isCollapsed && endLineNumber > lastHiddenLine) {
|
||||
lastHiddenLine = endLineNumber;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
};
|
||||
for (let region of toggledRegions) {
|
||||
let index = region.regionIndex;
|
||||
let editorDecorationId = this._editorDecorationIds[index];
|
||||
if (editorDecorationId && !processed[editorDecorationId]) {
|
||||
processed[editorDecorationId] = true;
|
||||
|
||||
updateDecorationsUntil(index); // update all decorations up to current index using the old dirtyRegionEndLine
|
||||
|
||||
let newCollapseState = !this._regions.isCollapsed(index);
|
||||
this._regions.setCollapsed(index, newCollapseState);
|
||||
accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState));
|
||||
|
||||
dirtyRegionEndLine = Math.max(dirtyRegionEndLine, this._regions.getEndLineNumber(index));
|
||||
}
|
||||
}
|
||||
updateDecorationsUntil(this._regions.length);
|
||||
});
|
||||
this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions });
|
||||
this._updateEventEmitter.fire({ model: this, collapseStateChanged: toggledRegions });
|
||||
}
|
||||
|
||||
public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void {
|
||||
@@ -75,20 +99,27 @@ export class FoldingModel {
|
||||
return false;
|
||||
};
|
||||
|
||||
let lastHiddenLine = -1;
|
||||
|
||||
let initRange = (index: number, isCollapsed: boolean) => {
|
||||
let startLineNumber = newRegions.getStartLineNumber(index);
|
||||
if (isCollapsed && isBlocked(startLineNumber, newRegions.getEndLineNumber(index))) {
|
||||
const startLineNumber = newRegions.getStartLineNumber(index);
|
||||
const endLineNumber = newRegions.getEndLineNumber(index);
|
||||
if (isCollapsed && isBlocked(startLineNumber, endLineNumber)) {
|
||||
isCollapsed = false;
|
||||
}
|
||||
newRegions.setCollapsed(index, isCollapsed);
|
||||
let maxColumn = this._textModel.getLineMaxColumn(startLineNumber);
|
||||
let decorationRange = {
|
||||
|
||||
const maxColumn = this._textModel.getLineMaxColumn(startLineNumber);
|
||||
const decorationRange = {
|
||||
startLineNumber: startLineNumber,
|
||||
startColumn: maxColumn,
|
||||
endLineNumber: startLineNumber,
|
||||
endColumn: maxColumn
|
||||
};
|
||||
newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(isCollapsed) });
|
||||
newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(isCollapsed, endLineNumber <= lastHiddenLine) });
|
||||
if (isCollapsed && endLineNumber > lastHiddenLine) {
|
||||
lastHiddenLine = endLineNumber;
|
||||
}
|
||||
};
|
||||
let i = 0;
|
||||
let nextCollapsed = () => {
|
||||
@@ -318,7 +349,7 @@ export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse:
|
||||
export function setCollapseStateUp(foldingModel: FoldingModel, doCollapse: boolean, lineNumbers: number[]): void {
|
||||
let toToggle: FoldingRegion[] = [];
|
||||
for (let lineNumber of lineNumbers) {
|
||||
let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region, ) => region.isCollapsed !== doCollapse);
|
||||
let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region,) => region.isCollapsed !== doCollapse);
|
||||
if (regions.length > 0) {
|
||||
toToggle.push(regions[0]);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,24 @@ interface ExpectedRegion {
|
||||
isCollapsed: boolean;
|
||||
}
|
||||
|
||||
interface ExpectedDecoration {
|
||||
line: number;
|
||||
type: 'hidden' | 'collapsed' | 'expanded';
|
||||
}
|
||||
|
||||
export class TestDecorationProvider {
|
||||
|
||||
private testDecorator = ModelDecorationOptions.register({
|
||||
private static readonly collapsedDecoration = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
linesDecorationsClassName: 'folding'
|
||||
});
|
||||
|
||||
private static readonly expandedDecoration = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
linesDecorationsClassName: 'folding'
|
||||
});
|
||||
|
||||
private static readonly hiddenDecoration = ModelDecorationOptions.register({
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
linesDecorationsClassName: 'folding'
|
||||
});
|
||||
@@ -31,8 +46,14 @@ export class TestDecorationProvider {
|
||||
constructor(private model: ITextModel) {
|
||||
}
|
||||
|
||||
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
|
||||
return this.testDecorator;
|
||||
getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions {
|
||||
if (isHidden) {
|
||||
return TestDecorationProvider.hiddenDecoration;
|
||||
}
|
||||
if (isCollapsed) {
|
||||
return TestDecorationProvider.collapsedDecoration;
|
||||
}
|
||||
return TestDecorationProvider.expandedDecoration;
|
||||
}
|
||||
|
||||
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] {
|
||||
@@ -42,6 +63,21 @@ export class TestDecorationProvider {
|
||||
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): (T | null) {
|
||||
return this.model.changeDecorations(callback);
|
||||
}
|
||||
|
||||
getDecorations(): ExpectedDecoration[] {
|
||||
const decorations = this.model.getAllDecorations();
|
||||
const res: ExpectedDecoration[] = [];
|
||||
for (let decoration of decorations) {
|
||||
if (decoration.options === TestDecorationProvider.hiddenDecoration) {
|
||||
res.push({ line: decoration.range.startLineNumber, type: 'hidden' });
|
||||
} else if (decoration.options === TestDecorationProvider.collapsedDecoration) {
|
||||
res.push({ line: decoration.range.startLineNumber, type: 'collapsed' });
|
||||
} else if (decoration.options === TestDecorationProvider.expandedDecoration) {
|
||||
res.push({ line: decoration.range.startLineNumber, type: 'expanded' });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
suite('Folding Model', () => {
|
||||
@@ -49,6 +85,10 @@ suite('Folding Model', () => {
|
||||
return { startLineNumber, endLineNumber, isCollapsed };
|
||||
}
|
||||
|
||||
function d(line: number, type: 'hidden' | 'collapsed' | 'expanded'): ExpectedDecoration {
|
||||
return { line, type };
|
||||
}
|
||||
|
||||
function assertRegion(actual: FoldingRegion | null, expected: ExpectedRegion | null, message?: string) {
|
||||
assert.equal(!!actual, !!expected, message);
|
||||
if (actual && expected) {
|
||||
@@ -78,6 +118,11 @@ suite('Folding Model', () => {
|
||||
assert.deepEqual(actualRanges, expectedRegions, message);
|
||||
}
|
||||
|
||||
function assertDecorations(foldingModel: FoldingModel, expectedDecoration: ExpectedDecoration[], message?: string) {
|
||||
const decorationProvider = foldingModel.decorationProvider as TestDecorationProvider;
|
||||
assert.deepEqual(decorationProvider.getDecorations(), expectedDecoration, message);
|
||||
}
|
||||
|
||||
function assertRegions(actual: FoldingRegion[], expectedRegions: ExpectedRegion[], message?: string) {
|
||||
assert.deepEqual(actual.map(r => ({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, isCollapsed: r.isCollapsed })), expectedRegions, message);
|
||||
}
|
||||
@@ -672,4 +717,65 @@ suite('Folding Model', () => {
|
||||
|
||||
});
|
||||
|
||||
test('folding decoration', () => {
|
||||
let lines = [
|
||||
/* 1*/ 'class A {',
|
||||
/* 2*/ ' void foo() {',
|
||||
/* 3*/ ' if (true) {',
|
||||
/* 4*/ ' hoo();',
|
||||
/* 5*/ ' }',
|
||||
/* 6*/ ' }',
|
||||
/* 7*/ '}'];
|
||||
|
||||
let textModel = createTextModel(lines.join('\n'));
|
||||
try {
|
||||
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
|
||||
|
||||
let ranges = computeRanges(textModel, false, undefined);
|
||||
foldingModel.update(ranges);
|
||||
|
||||
let r1 = r(1, 6, false);
|
||||
let r2 = r(2, 5, false);
|
||||
let r3 = r(3, 4, false);
|
||||
|
||||
assertRanges(foldingModel, [r1, r2, r3]);
|
||||
assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'expanded'), d(3, 'expanded')]);
|
||||
|
||||
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2)!]);
|
||||
|
||||
assertRanges(foldingModel, [r1, r(2, 5, true), r3]);
|
||||
assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);
|
||||
|
||||
foldingModel.update(ranges);
|
||||
|
||||
assertRanges(foldingModel, [r1, r(2, 5, true), r3]);
|
||||
assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);
|
||||
|
||||
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!]);
|
||||
|
||||
assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]);
|
||||
assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]);
|
||||
|
||||
foldingModel.update(ranges);
|
||||
|
||||
assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]);
|
||||
assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]);
|
||||
|
||||
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!, foldingModel.getRegionAtLine(3)!]);
|
||||
|
||||
assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]);
|
||||
assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);
|
||||
|
||||
foldingModel.update(ranges);
|
||||
|
||||
assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]);
|
||||
assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);
|
||||
|
||||
textModel.dispose();
|
||||
} finally {
|
||||
textModel.dispose();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
||||
export function alertFormattingEdits(edits: ISingleEditOperation[]): void {
|
||||
|
||||
@@ -111,7 +112,7 @@ export abstract class FormattingConflicts {
|
||||
if (formatter.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const { value: selector } = FormattingConflicts._selectors.iterator().next();
|
||||
const selector = Iterable.first(FormattingConflicts._selectors);
|
||||
if (selector) {
|
||||
return await selector(formatter, document, mode);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerColor, oneOf, textLinkForeground, editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
@@ -246,13 +246,13 @@ export class MarkerNavigationWidget extends PeekViewWidget {
|
||||
this._severity = MarkerSeverity.Warning;
|
||||
this._backgroundColor = Color.white;
|
||||
|
||||
this._applyTheme(_themeService.getTheme());
|
||||
this._callOnDispose.add(_themeService.onThemeChange(this._applyTheme.bind(this)));
|
||||
this._applyTheme(_themeService.getColorTheme());
|
||||
this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
|
||||
|
||||
this.create();
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
private _applyTheme(theme: IColorTheme) {
|
||||
this._backgroundColor = theme.getColor(editorMarkerNavigationBackground);
|
||||
let colorId = editorMarkerNavigationError;
|
||||
if (this._severity === MarkerSeverity.Warning) {
|
||||
@@ -327,7 +327,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
|
||||
|
||||
// update frame color (only applied on 'show')
|
||||
this._severity = marker.severity;
|
||||
this._applyTheme(this._themeService.getTheme());
|
||||
this._applyTheme(this._themeService.getColorTheme());
|
||||
|
||||
// show
|
||||
let range = Range.lift(marker);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -31,7 +30,7 @@ export class ClickLinkMouseEvent {
|
||||
this.target = source.target;
|
||||
this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier);
|
||||
this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier);
|
||||
this.isNoneOrSingleMouseDown = (browser.isIE || source.event.detail <= 1); // IE does not support event.detail properly
|
||||
this.isNoneOrSingleMouseDown = (source.event.detail <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +108,9 @@ export class ClickLinkGesture extends Disposable {
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _opts: ClickLinkOptions;
|
||||
|
||||
private lastMouseMoveEvent: ClickLinkMouseEvent | null;
|
||||
private hasTriggerKeyOnMouseDown: boolean;
|
||||
private _lastMouseMoveEvent: ClickLinkMouseEvent | null;
|
||||
private _hasTriggerKeyOnMouseDown: boolean;
|
||||
private _lineNumberOnMouseDown: number;
|
||||
|
||||
constructor(editor: ICodeEditor) {
|
||||
super();
|
||||
@@ -118,8 +118,9 @@ export class ClickLinkGesture extends Disposable {
|
||||
this._editor = editor;
|
||||
this._opts = createOptions(this._editor.getOption(EditorOption.multiCursorModifier));
|
||||
|
||||
this.lastMouseMoveEvent = null;
|
||||
this.hasTriggerKeyOnMouseDown = false;
|
||||
this._lastMouseMoveEvent = null;
|
||||
this._hasTriggerKeyOnMouseDown = false;
|
||||
this._lineNumberOnMouseDown = 0;
|
||||
|
||||
this._register(this._editor.onDidChangeConfiguration((e) => {
|
||||
if (e.hasChanged(EditorOption.multiCursorModifier)) {
|
||||
@@ -128,77 +129,80 @@ export class ClickLinkGesture extends Disposable {
|
||||
return;
|
||||
}
|
||||
this._opts = newOpts;
|
||||
this.lastMouseMoveEvent = null;
|
||||
this.hasTriggerKeyOnMouseDown = false;
|
||||
this._lastMouseMoveEvent = null;
|
||||
this._hasTriggerKeyOnMouseDown = false;
|
||||
this._lineNumberOnMouseDown = 0;
|
||||
this._onCancel.fire();
|
||||
}
|
||||
}));
|
||||
this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this.onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts))));
|
||||
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseDrag(() => this.resetHandler()));
|
||||
this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts))));
|
||||
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this._onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts))));
|
||||
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this._onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts))));
|
||||
this._register(this._editor.onMouseDrag(() => this._resetHandler()));
|
||||
|
||||
this._register(this._editor.onDidChangeCursorSelection((e) => this.onDidChangeCursorSelection(e)));
|
||||
this._register(this._editor.onDidChangeModel((e) => this.resetHandler()));
|
||||
this._register(this._editor.onDidChangeModelContent(() => this.resetHandler()));
|
||||
this._register(this._editor.onDidChangeCursorSelection((e) => this._onDidChangeCursorSelection(e)));
|
||||
this._register(this._editor.onDidChangeModel((e) => this._resetHandler()));
|
||||
this._register(this._editor.onDidChangeModelContent(() => this._resetHandler()));
|
||||
this._register(this._editor.onDidScrollChange((e) => {
|
||||
if (e.scrollTopChanged || e.scrollLeftChanged) {
|
||||
this.resetHandler();
|
||||
this._resetHandler();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void {
|
||||
private _onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void {
|
||||
if (e.selection && e.selection.startColumn !== e.selection.endColumn) {
|
||||
this.resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827)
|
||||
this._resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827)
|
||||
}
|
||||
}
|
||||
|
||||
private onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void {
|
||||
this.lastMouseMoveEvent = mouseEvent;
|
||||
private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void {
|
||||
this._lastMouseMoveEvent = mouseEvent;
|
||||
|
||||
this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]);
|
||||
}
|
||||
|
||||
private onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void {
|
||||
private _onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void {
|
||||
// We need to record if we had the trigger key on mouse down because someone might select something in the editor
|
||||
// holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then
|
||||
// release the mouse button without wanting to do the navigation.
|
||||
// With this flag we prevent goto definition if the mouse was down before the trigger key was pressed.
|
||||
this.hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier;
|
||||
this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier;
|
||||
this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
|
||||
}
|
||||
|
||||
private onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void {
|
||||
if (this.hasTriggerKeyOnMouseDown) {
|
||||
private _onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void {
|
||||
const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
|
||||
if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) {
|
||||
this._onExecute.fire(mouseEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private onEditorKeyDown(e: ClickLinkKeyboardEvent): void {
|
||||
private _onEditorKeyDown(e: ClickLinkKeyboardEvent): void {
|
||||
if (
|
||||
this.lastMouseMoveEvent
|
||||
this._lastMouseMoveEvent
|
||||
&& (
|
||||
e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition)
|
||||
|| (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side)
|
||||
)
|
||||
) {
|
||||
this._onMouseMoveOrRelevantKeyDown.fire([this.lastMouseMoveEvent, e]);
|
||||
this._onMouseMoveOrRelevantKeyDown.fire([this._lastMouseMoveEvent, e]);
|
||||
} else if (e.hasTriggerModifier) {
|
||||
this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration
|
||||
}
|
||||
}
|
||||
|
||||
private onEditorKeyUp(e: ClickLinkKeyboardEvent): void {
|
||||
private _onEditorKeyUp(e: ClickLinkKeyboardEvent): void {
|
||||
if (e.keyCodeIsTriggerKey) {
|
||||
this._onCancel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
private resetHandler(): void {
|
||||
this.lastMouseMoveEvent = null;
|
||||
this.hasTriggerKeyOnMouseDown = false;
|
||||
private _resetHandler(): void {
|
||||
this._lastMouseMoveEvent = null;
|
||||
this._hasTriggerKeyOnMouseDown = false;
|
||||
this._onCancel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,10 +217,10 @@ export abstract class ReferencesController implements IEditorContribution {
|
||||
}
|
||||
|
||||
closeWidget(focusEditor = true): void {
|
||||
this._referenceSearchVisible.reset();
|
||||
this._disposables.clear();
|
||||
dispose(this._widget);
|
||||
dispose(this._model);
|
||||
this._referenceSearchVisible.reset();
|
||||
this._disposables.clear();
|
||||
this._widget = undefined;
|
||||
this._model = undefined;
|
||||
if (focusEditor) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { WorkbenchAsyncDataTree, IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService';
|
||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import * as peekView from 'vs/editor/contrib/peekView/peekView';
|
||||
import { FileReferences, OneReference, ReferencesModel } from '../referencesModel';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
@@ -221,8 +221,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
) {
|
||||
super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true });
|
||||
|
||||
this._applyTheme(themeService.getTheme());
|
||||
this._callOnDispose.add(themeService.onThemeChange(this._applyTheme.bind(this)));
|
||||
this._applyTheme(themeService.getColorTheme());
|
||||
this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
|
||||
this._peekViewService.addExclusiveWidget(editor, this);
|
||||
this.create();
|
||||
}
|
||||
@@ -239,7 +239,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
private _applyTheme(theme: IColorTheme) {
|
||||
const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent;
|
||||
this.style({
|
||||
arrowColor: borderColor,
|
||||
|
||||
@@ -960,7 +960,7 @@ export abstract class AbstractCaseAction extends EditorAction {
|
||||
let selection = selections[i];
|
||||
if (selection.isEmpty()) {
|
||||
let cursor = selection.getStartPosition();
|
||||
let word = model.getWordAtPosition(cursor);
|
||||
const word = editor.getConfiguredWordAtPosition(cursor);
|
||||
|
||||
if (!word) {
|
||||
continue;
|
||||
|
||||
@@ -101,7 +101,7 @@ class LinkOccurrence {
|
||||
}
|
||||
}
|
||||
|
||||
class LinkDetector implements IEditorContribution {
|
||||
export class LinkDetector implements IEditorContribution {
|
||||
|
||||
public static readonly ID: string = 'editor.linkDetector';
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ export class MultiCursorSession {
|
||||
|
||||
if (s.isEmpty()) {
|
||||
// selection is empty => expand to current word
|
||||
const word = editor.getModel().getWordAtPosition(s.getStartPosition());
|
||||
const word = editor.getConfiguredWordAtPosition(s.getStartPosition());
|
||||
if (!word) {
|
||||
return null;
|
||||
}
|
||||
@@ -505,7 +505,7 @@ export class MultiCursorSelectionController extends Disposable implements IEdito
|
||||
if (!selection.isEmpty()) {
|
||||
return selection;
|
||||
}
|
||||
const word = model.getWordAtPosition(selection.getStartPosition());
|
||||
const word = this._editor.getConfiguredWordAtPosition(selection.getStartPosition());
|
||||
if (!word) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ suite('Multicursor selection', () => {
|
||||
'var z = (3 * 5)',
|
||||
], { serviceCollection: serviceCollection }, (editor, cursor) => {
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let selectHighlightsAction = new SelectHighlightsAction();
|
||||
|
||||
editor.setSelection(new Selection(2, 9, 2, 16));
|
||||
@@ -110,8 +110,8 @@ suite('Multicursor selection', () => {
|
||||
'nothing'
|
||||
], { serviceCollection: serviceCollection }, (editor, cursor) => {
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let selectHighlightsAction = new SelectHighlightsAction();
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
@@ -144,8 +144,8 @@ suite('Multicursor selection', () => {
|
||||
'rty'
|
||||
], { serviceCollection: serviceCollection }, (editor, cursor) => {
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 3, 4));
|
||||
@@ -172,8 +172,8 @@ suite('Multicursor selection', () => {
|
||||
'abcabc',
|
||||
], { serviceCollection: serviceCollection }, (editor, cursor) => {
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 1, 4));
|
||||
@@ -229,8 +229,8 @@ suite('Multicursor selection', () => {
|
||||
|
||||
editor.getModel()!.setEOL(EndOfLineSequence.CRLF);
|
||||
|
||||
let findController = editor.registerAndInstantiateContribution<CommonFindController>(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 3, 4));
|
||||
@@ -252,8 +252,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.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution<MultiCursorSelectionController>(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController);
|
||||
let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController);
|
||||
|
||||
callback(editor, findController);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { IOptions, IStyles, ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ContextKeyExpr, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
@@ -57,7 +57,7 @@ registerSingleton(IPeekViewService, class implements IPeekViewService {
|
||||
|
||||
export namespace PeekContext {
|
||||
export const inPeekEditor = new RawContextKey<boolean>('inReferenceSearchEditor', true);
|
||||
export const notInPeekEditor: ContextKeyExpr = inPeekEditor.toNegated();
|
||||
export const notInPeekEditor = inPeekEditor.toNegated();
|
||||
}
|
||||
|
||||
class PeekContextController implements IEditorContribution {
|
||||
|
||||
206
src/vs/editor/contrib/quickAccess/gotoLine.ts
Normal file
206
src/vs/editor/contrib/quickAccess/gotoLine.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore, toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { AbstractEditorQuickAccessProvider } from 'vs/editor/contrib/quickAccess/quickAccess';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
|
||||
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial<IPosition> { }
|
||||
|
||||
export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorQuickAccessProvider {
|
||||
|
||||
static PREFIX = ':';
|
||||
|
||||
provide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Disable filtering & sorting, we control the results
|
||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||
|
||||
// Provide based on current active editor
|
||||
let pickerDisposable = this.doProvide(picker, token);
|
||||
disposables.add(toDisposable(() => pickerDisposable.dispose()));
|
||||
|
||||
// Re-create whenever the active editor changes
|
||||
disposables.add(this.onDidActiveTextEditorControlChange(() => {
|
||||
pickerDisposable.dispose();
|
||||
pickerDisposable = this.doProvide(picker, token);
|
||||
}));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private doProvide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
||||
|
||||
// With text control
|
||||
if (this.activeTextEditorControl) {
|
||||
return this.doProvideWithTextEditor(this.activeTextEditorControl, picker, token);
|
||||
}
|
||||
|
||||
// Without text control
|
||||
return this.doProvideWithoutTextEditor(picker);
|
||||
}
|
||||
|
||||
private doProvideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
|
||||
const label = localize('cannotRunGotoLine', "Open a text file first to go to a line.");
|
||||
picker.items = [{ label }];
|
||||
picker.ariaLabel = label;
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
private doProvideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Restore any view state if this picker was closed
|
||||
// without actually going to a line
|
||||
const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
|
||||
once(token.onCancellationRequested)(() => {
|
||||
if (lastKnownEditorViewState) {
|
||||
editor.restoreViewState(lastKnownEditorViewState);
|
||||
}
|
||||
});
|
||||
|
||||
// Goto line once picked
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (item) {
|
||||
if (!this.isValidLineNumber(editor, item.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gotoLine(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
|
||||
|
||||
picker.hide();
|
||||
}
|
||||
}));
|
||||
|
||||
// React to picker changes
|
||||
const updatePickerAndEditor = () => {
|
||||
const position = this.parsePosition(editor, picker.value.trim().substr(AbstractGotoLineQuickAccessProvider.PREFIX.length));
|
||||
const label = this.getPickLabel(editor, position.lineNumber, position.column);
|
||||
|
||||
// Picker
|
||||
picker.items = [{
|
||||
lineNumber: position.lineNumber,
|
||||
column: position.column,
|
||||
label
|
||||
}];
|
||||
|
||||
// ARIA Label
|
||||
picker.ariaLabel = label;
|
||||
|
||||
// Clear decorations for invalid range
|
||||
if (!this.isValidLineNumber(editor, position.lineNumber)) {
|
||||
this.clearDecorations(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reveal
|
||||
const range = this.toRange(position.lineNumber, position.column);
|
||||
editor.revealRangeInCenter(range, ScrollType.Smooth);
|
||||
|
||||
// Decorate
|
||||
this.addDecorations(editor, range);
|
||||
};
|
||||
updatePickerAndEditor();
|
||||
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
|
||||
|
||||
// Clean up decorations on dispose
|
||||
disposables.add(toDisposable(() => this.clearDecorations(editor)));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private toRange(lineNumber = 1, column = 1): IRange {
|
||||
return {
|
||||
startLineNumber: lineNumber,
|
||||
startColumn: column,
|
||||
endLineNumber: lineNumber,
|
||||
endColumn: column
|
||||
};
|
||||
}
|
||||
|
||||
private parsePosition(editor: IEditor, value: string): IPosition {
|
||||
|
||||
// Support line-col formats of `line,col`, `line:col`, `line#col`
|
||||
const numbers = value.split(/,|:|#/).map(part => parseInt(part, 10)).filter(part => !isNaN(part));
|
||||
const endLine = this.lineCount(editor) + 1;
|
||||
|
||||
return {
|
||||
lineNumber: numbers[0] > 0 ? numbers[0] : endLine + numbers[0],
|
||||
column: numbers[1]
|
||||
};
|
||||
}
|
||||
|
||||
private getPickLabel(editor: IEditor, lineNumber: number, column: number | undefined): string {
|
||||
|
||||
// Location valid: indicate this as picker label
|
||||
if (this.isValidLineNumber(editor, lineNumber)) {
|
||||
if (this.isValidColumn(editor, lineNumber, column)) {
|
||||
return localize('gotoLineColumnLabel', "Go to line {0} and column {1}.", lineNumber, column);
|
||||
}
|
||||
|
||||
return localize('gotoLineLabel', "Go to line {0}.", lineNumber);
|
||||
}
|
||||
|
||||
// Location invalid: show generic label
|
||||
const position = editor.getPosition() || { lineNumber: 1, column: 1 };
|
||||
const lineCount = this.lineCount(editor);
|
||||
if (lineCount > 1) {
|
||||
return localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Column: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, lineCount);
|
||||
}
|
||||
|
||||
return localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column);
|
||||
}
|
||||
|
||||
private isValidLineNumber(editor: IEditor, lineNumber: number | undefined): boolean {
|
||||
if (!lineNumber || typeof lineNumber !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lineNumber > 0 && lineNumber <= this.lineCount(editor);
|
||||
}
|
||||
|
||||
private isValidColumn(editor: IEditor, lineNumber: number, column: number | undefined): boolean {
|
||||
if (!column || typeof column !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const model = this.getModel(editor);
|
||||
if (!model) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const positionCandidate = { lineNumber, column };
|
||||
|
||||
return model.validatePosition(positionCandidate).equals(positionCandidate);
|
||||
}
|
||||
|
||||
private lineCount(editor: IEditor): number {
|
||||
return this.getModel(editor)?.getLineCount() ?? 0;
|
||||
}
|
||||
|
||||
private getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
|
||||
return isDiffEditor(editor) ?
|
||||
editor.getModel()?.modified :
|
||||
editor.getModel() as ITextModel;
|
||||
}
|
||||
|
||||
protected gotoLine(editor: IEditor, range: IRange, keyMods: IKeyMods): void {
|
||||
editor.setSelection(range);
|
||||
editor.revealRangeInCenter(range, ScrollType.Smooth);
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
104
src/vs/editor/contrib/quickAccess/quickAccess.ts
Normal file
104
src/vs/editor/contrib/quickAccess/quickAccess.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
interface IEditorLineDecoration {
|
||||
rangeHighlightId: string;
|
||||
overviewRulerDecorationId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reusable quick access provider for the editor with support for adding decorations.
|
||||
*/
|
||||
export abstract class AbstractEditorQuickAccessProvider implements IQuickAccessProvider {
|
||||
|
||||
/**
|
||||
* Subclasses to provide an event when the active editor control changes.
|
||||
*/
|
||||
abstract readonly onDidActiveTextEditorControlChange: Event<void>;
|
||||
|
||||
/**
|
||||
* Subclasses to provide the current active editor control.
|
||||
*/
|
||||
abstract activeTextEditorControl: IEditor | undefined;
|
||||
|
||||
/**
|
||||
* Subclasses to implement the quick access picker.
|
||||
*/
|
||||
abstract provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
|
||||
|
||||
|
||||
//#region Decorations Utils
|
||||
|
||||
private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
|
||||
|
||||
protected addDecorations(editor: IEditor, range: IRange): void {
|
||||
editor.changeDecorations(changeAccessor => {
|
||||
|
||||
// Reset old decorations if any
|
||||
const deleteDecorations: string[] = [];
|
||||
if (this.rangeHighlightDecorationId) {
|
||||
deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
|
||||
deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
|
||||
|
||||
this.rangeHighlightDecorationId = undefined;
|
||||
}
|
||||
|
||||
// Add new decorations for the range
|
||||
const newDecorations: IModelDeltaDecoration[] = [
|
||||
|
||||
// highlight the entire line on the range
|
||||
{
|
||||
range,
|
||||
options: {
|
||||
className: 'rangeHighlight',
|
||||
isWholeLine: true
|
||||
}
|
||||
},
|
||||
|
||||
// also add overview ruler highlight
|
||||
{
|
||||
range,
|
||||
options: {
|
||||
overviewRuler: {
|
||||
color: themeColorFromId(overviewRulerRangeHighlight),
|
||||
position: OverviewRulerLane.Full
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
|
||||
|
||||
this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
|
||||
});
|
||||
}
|
||||
|
||||
protected clearDecorations(editor: IEditor): void {
|
||||
const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
|
||||
if (rangeHighlightDecorationId) {
|
||||
editor.changeDecorations(changeAccessor => {
|
||||
changeAccessor.deltaDecorations([
|
||||
rangeHighlightDecorationId.overviewRulerDecorationId,
|
||||
rangeHighlightDecorationId.rangeHighlightId
|
||||
], []);
|
||||
});
|
||||
|
||||
this.rangeHighlightDecorationId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { inputBackground, inputBorder, inputForeground, widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { toggleClass } from 'vs/base/browser/dom';
|
||||
@@ -53,7 +53,7 @@ export class RenameInputField implements IContentWidget {
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.add(_themeService.onThemeChange(this._updateStyles, this));
|
||||
this._disposables.add(_themeService.onDidColorThemeChange(this._updateStyles, this));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -88,12 +88,12 @@ export class RenameInputField implements IContentWidget {
|
||||
this._disposables.add(this._keybindingService.onDidUpdateKeybindings(updateLabel));
|
||||
|
||||
this._updateFont();
|
||||
this._updateStyles(this._themeService.getTheme());
|
||||
this._updateStyles(this._themeService.getColorTheme());
|
||||
}
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
private _updateStyles(theme: ITheme): void {
|
||||
private _updateStyles(theme: IColorTheme): void {
|
||||
if (!this._input || !this._domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings';
|
||||
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
@@ -244,15 +244,15 @@ export class TimeBasedVariableResolver implements VariableResolver {
|
||||
} else if (name === 'CURRENT_YEAR_SHORT') {
|
||||
return String(new Date().getFullYear()).slice(-2);
|
||||
} else if (name === 'CURRENT_MONTH') {
|
||||
return pad((new Date().getMonth().valueOf() + 1), 2);
|
||||
return String(new Date().getMonth().valueOf() + 1).padStart(2, '0');
|
||||
} else if (name === 'CURRENT_DATE') {
|
||||
return pad(new Date().getDate().valueOf(), 2);
|
||||
return String(new Date().getDate().valueOf()).padStart(2, '0');
|
||||
} else if (name === 'CURRENT_HOUR') {
|
||||
return pad(new Date().getHours().valueOf(), 2);
|
||||
return String(new Date().getHours().valueOf()).padStart(2, '0');
|
||||
} else if (name === 'CURRENT_MINUTE') {
|
||||
return pad(new Date().getMinutes().valueOf(), 2);
|
||||
return String(new Date().getMinutes().valueOf()).padStart(2, '0');
|
||||
} else if (name === 'CURRENT_SECOND') {
|
||||
return pad(new Date().getSeconds().valueOf(), 2);
|
||||
return String(new Date().getSeconds().valueOf()).padStart(2, '0');
|
||||
} else if (name === 'CURRENT_DAY_NAME') {
|
||||
return TimeBasedVariableResolver.dayNames[new Date().getDay()];
|
||||
} else if (name === 'CURRENT_DAY_NAME_SHORT') {
|
||||
@@ -300,7 +300,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
|
||||
}
|
||||
|
||||
let filename = path.basename(workspaceIdentifier.configPath.path);
|
||||
if (endsWith(filename, WORKSPACE_EXTENSION)) {
|
||||
if (filename.endsWith(WORKSPACE_EXTENSION)) {
|
||||
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||
}
|
||||
return filename;
|
||||
@@ -312,7 +312,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
|
||||
|
||||
let filename = path.basename(workspaceIdentifier.configPath.path);
|
||||
let folderpath = workspaceIdentifier.configPath.fsPath;
|
||||
if (endsWith(folderpath, filename)) {
|
||||
if (folderpath.endsWith(filename)) {
|
||||
folderpath = folderpath.substr(0, folderpath.length - filename.length - 1);
|
||||
}
|
||||
return (folderpath ? normalizeDriveLetter(folderpath) : '/');
|
||||
|
||||
@@ -45,7 +45,7 @@ suite('SnippetController', () => {
|
||||
editor.getModel()!.updateOptions({
|
||||
insertSpaces: false
|
||||
});
|
||||
let snippetController = editor.registerAndInstantiateContribution<TestSnippetController>(TestSnippetController.ID, TestSnippetController);
|
||||
let snippetController = editor.registerAndInstantiateContribution(TestSnippetController.ID, TestSnippetController);
|
||||
let template = [
|
||||
'for (var ${1:index}; $1 < ${2:array}.length; $1++) {',
|
||||
'\tvar element = $2[$1];',
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label {
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label {
|
||||
|
||||
@@ -9,15 +9,18 @@ import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { CompletionItemKind, completionKindFromString } from 'vs/editor/common/modes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { CompletionItem } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
|
||||
export abstract class Memory {
|
||||
|
||||
constructor(readonly name: MemMode) { }
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number {
|
||||
if (items.length === 0) {
|
||||
return 0;
|
||||
@@ -46,6 +49,10 @@ export abstract class Memory {
|
||||
|
||||
export class NoMemory extends Memory {
|
||||
|
||||
constructor() {
|
||||
super('first');
|
||||
}
|
||||
|
||||
memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void {
|
||||
// no-op
|
||||
}
|
||||
@@ -67,6 +74,10 @@ export interface MemItem {
|
||||
|
||||
export class LRUMemory extends Memory {
|
||||
|
||||
constructor() {
|
||||
super('recentlyUsed');
|
||||
}
|
||||
|
||||
private _cache = new LRUCache<string, MemItem>(300, 0.66);
|
||||
private _seq = 0;
|
||||
|
||||
@@ -143,6 +154,10 @@ export class LRUMemory extends Memory {
|
||||
|
||||
export class PrefixMemory extends Memory {
|
||||
|
||||
constructor() {
|
||||
super('recentlyUsedByPrefix');
|
||||
}
|
||||
|
||||
private _trie = TernarySearchTree.forStrings<MemItem>();
|
||||
private _seq = 0;
|
||||
|
||||
@@ -206,85 +221,86 @@ export class PrefixMemory extends Memory {
|
||||
|
||||
export type MemMode = 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix';
|
||||
|
||||
export class SuggestMemoryService extends Disposable implements ISuggestMemoryService {
|
||||
export class SuggestMemoryService implements ISuggestMemoryService {
|
||||
|
||||
private static readonly _strategyCtors = new Map<MemMode, { new(): Memory }>([
|
||||
['recentlyUsedByPrefix', PrefixMemory],
|
||||
['recentlyUsed', LRUMemory],
|
||||
['first', NoMemory]
|
||||
]);
|
||||
|
||||
private static readonly _storagePrefix = 'suggest/memories';
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
|
||||
private readonly _persistSoon: RunOnceScheduler;
|
||||
private _mode!: MemMode;
|
||||
private _shareMem!: boolean;
|
||||
private _strategy!: Memory;
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private _strategy?: Memory;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
const update = () => {
|
||||
const mode = this._configService.getValue<MemMode>('editor.suggestSelection');
|
||||
const share = this._configService.getValue<boolean>('editor.suggest.shareSuggestSelections');
|
||||
this._update(mode, share, false);
|
||||
};
|
||||
|
||||
this._persistSoon = this._register(new RunOnceScheduler(() => this._saveState(), 500));
|
||||
this._register(_storageService.onWillSaveState(e => {
|
||||
this._persistSoon = new RunOnceScheduler(() => this._saveState(), 500);
|
||||
this._disposables.add(_storageService.onWillSaveState(e => {
|
||||
if (e.reason === WillSaveStateReason.SHUTDOWN) {
|
||||
this._saveState();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._configService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('editor.suggestSelection') || e.affectsConfiguration('editor.suggest.shareSuggestSelections')) {
|
||||
update();
|
||||
}
|
||||
}));
|
||||
this._register(this._storageService.onDidChangeStorage(e => {
|
||||
if (e.scope === StorageScope.GLOBAL && e.key.indexOf(this._storagePrefix) === 0) {
|
||||
if (!document.hasFocus()) {
|
||||
// windows that aren't focused have to drop their current
|
||||
// storage value and accept what's stored now
|
||||
this._update(this._mode, this._shareMem, true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
update();
|
||||
}
|
||||
|
||||
private _update(mode: MemMode, shareMem: boolean, force: boolean): void {
|
||||
if (!force && this._mode === mode && this._shareMem === shareMem) {
|
||||
return;
|
||||
}
|
||||
this._shareMem = shareMem;
|
||||
this._mode = mode;
|
||||
this._strategy = mode === 'recentlyUsedByPrefix' ? new PrefixMemory() : mode === 'recentlyUsed' ? new LRUMemory() : new NoMemory();
|
||||
|
||||
try {
|
||||
const scope = shareMem ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
const raw = this._storageService.get(`${this._storagePrefix}/${this._mode}`, scope);
|
||||
if (raw) {
|
||||
this._strategy.fromJSON(JSON.parse(raw));
|
||||
}
|
||||
} catch (e) {
|
||||
// things can go wrong with JSON...
|
||||
}
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._persistSoon.dispose();
|
||||
}
|
||||
|
||||
memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void {
|
||||
this._strategy.memorize(model, pos, item);
|
||||
this._withStrategy(model, pos).memorize(model, pos, item);
|
||||
this._persistSoon.schedule();
|
||||
}
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number {
|
||||
return this._strategy.select(model, pos, items);
|
||||
return this._withStrategy(model, pos).select(model, pos, items);
|
||||
}
|
||||
|
||||
private _withStrategy(model: ITextModel, pos: IPosition): Memory {
|
||||
|
||||
const mode = this._configService.getValue<MemMode>('editor.suggestSelection', {
|
||||
overrideIdentifier: this._modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(pos.lineNumber, pos.column))?.language,
|
||||
resource: model.uri
|
||||
});
|
||||
|
||||
if (this._strategy?.name !== mode) {
|
||||
|
||||
this._saveState();
|
||||
const ctor = SuggestMemoryService._strategyCtors.get(mode) || NoMemory;
|
||||
this._strategy = new ctor();
|
||||
|
||||
try {
|
||||
const share = this._configService.getValue<boolean>('editor.suggest.shareSuggestSelections');
|
||||
const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
const raw = this._storageService.get(`${SuggestMemoryService._storagePrefix}/${mode}`, scope);
|
||||
if (raw) {
|
||||
this._strategy.fromJSON(JSON.parse(raw));
|
||||
}
|
||||
} catch (e) {
|
||||
// things can go wrong with JSON...
|
||||
}
|
||||
}
|
||||
|
||||
return this._strategy;
|
||||
}
|
||||
|
||||
private _saveState() {
|
||||
const raw = JSON.stringify(this._strategy);
|
||||
const scope = this._shareMem ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
this._storageService.store(`${this._storagePrefix}/${this._mode}`, raw, scope);
|
||||
if (this._strategy) {
|
||||
const share = this._configService.getValue<boolean>('editor.suggest.shareSuggestSelections');
|
||||
const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
const raw = JSON.stringify(this._strategy);
|
||||
this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu }
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
@@ -144,11 +144,10 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
|
||||
const text = append(container, $('.contents'));
|
||||
const main = append(text, $('.main'));
|
||||
|
||||
data.iconContainer = append(main, $('.icon-label.codicon'));
|
||||
data.left = append(main, $('span.left'));
|
||||
data.right = append(main, $('span.right'));
|
||||
|
||||
data.iconContainer = append(data.left, $('.icon-label.codicon'));
|
||||
|
||||
data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportCodicons: true });
|
||||
data.disposables.add(data.iconLabel);
|
||||
|
||||
@@ -211,7 +210,7 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
|
||||
data.iconContainer.className = 'icon hide';
|
||||
data.colorspan.style.backgroundColor = color[0];
|
||||
|
||||
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) {
|
||||
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getFileIconTheme().hasFileIcons) {
|
||||
// special logic for 'file' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
@@ -219,7 +218,7 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
|
||||
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) {
|
||||
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getFileIconTheme().hasFolderIcons) {
|
||||
// special logic for 'folder' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
@@ -474,7 +473,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
readonly allowEditorOverflow = true;
|
||||
readonly suppressMouseDown = false;
|
||||
|
||||
private state: State | null = null;
|
||||
private state: State = State.Hidden;
|
||||
private isAddedAsContentWidget: boolean = false;
|
||||
private isAuto: boolean = false;
|
||||
private loadingTimeout: IDisposable = Disposable.None;
|
||||
private currentSuggestionDetails: CancelablePromise<void> | null = null;
|
||||
@@ -627,12 +627,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
listInactiveFocusBackground: editorSuggestWidgetSelectedBackground,
|
||||
listInactiveFocusOutline: activeContrastBorder
|
||||
}));
|
||||
this.toDispose.add(themeService.onThemeChange(t => this.onThemeChange(t)));
|
||||
this.toDispose.add(themeService.onDidColorThemeChange(t => this.onThemeChange(t)));
|
||||
this.toDispose.add(editor.onDidLayoutChange(() => this.onEditorLayoutChange()));
|
||||
this.toDispose.add(this.list.onMouseDown(e => this.onListMouseDownOrTap(e)));
|
||||
this.toDispose.add(this.list.onTap(e => this.onListMouseDownOrTap(e)));
|
||||
this.toDispose.add(this.list.onSelectionChange(e => this.onListSelection(e)));
|
||||
this.toDispose.add(this.list.onFocusChange(e => this.onListFocus(e)));
|
||||
this.toDispose.add(this.list.onDidChangeSelection(e => this.onListSelection(e)));
|
||||
this.toDispose.add(this.list.onDidChangeFocus(e => this.onListFocus(e)));
|
||||
this.toDispose.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()));
|
||||
this.toDispose.add(this.editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.suggest)) {
|
||||
@@ -645,10 +645,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(contextKeyService);
|
||||
this.ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService);
|
||||
|
||||
this.editor.addContentWidget(this);
|
||||
this.setState(State.Hidden);
|
||||
|
||||
this.onThemeChange(themeService.getTheme());
|
||||
this.onThemeChange(themeService.getColorTheme());
|
||||
|
||||
this.toDispose.add(addStandardDisposableListener(this.details.element, 'keydown', e => {
|
||||
this._onDetailsKeydown.fire(e);
|
||||
@@ -715,7 +712,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
private onThemeChange(theme: ITheme) {
|
||||
private onThemeChange(theme: IColorTheme) {
|
||||
const backgroundColor = theme.getColor(editorSuggestWidgetBackground);
|
||||
if (backgroundColor) {
|
||||
this.listElement.style.backgroundColor = backgroundColor.toString();
|
||||
@@ -811,6 +808,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isAddedAsContentWidget && state !== State.Hidden) {
|
||||
this.isAddedAsContentWidget = true;
|
||||
this.editor.addContentWidget(this);
|
||||
}
|
||||
|
||||
const stateChanged = this.state !== state;
|
||||
this.state = state;
|
||||
|
||||
@@ -1291,6 +1293,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.toDispose.dispose();
|
||||
this.loadingTimeout.dispose();
|
||||
this.showTimeout.dispose();
|
||||
this.editor.removeContentWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ suite('SuggestMemories', function () {
|
||||
test('AbstractMemory, select', function () {
|
||||
|
||||
const mem = new class extends Memory {
|
||||
constructor() {
|
||||
super('first');
|
||||
}
|
||||
memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void {
|
||||
throw new Error('Method not implemented.');
|
||||
} toJSON(): object {
|
||||
|
||||
Reference in New Issue
Block a user