mirror of
https://github.com/coder/code-server.git
synced 2026-05-09 05:47:26 +02:00
chore(vscode): update to 1.55.2
This commit is contained in:
@@ -7,6 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { isFirefox } from 'vs/base/browser/browser';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Command, EditorCommand, ICommandOptions, registerEditorCommand, MultiCommand, UndoCommand, RedoCommand, SelectAllCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
@@ -280,7 +281,7 @@ abstract class EditorOrNativeTextInputCommand {
|
||||
|
||||
constructor(target: MultiCommand) {
|
||||
// 1. handle case when focus is in editor.
|
||||
target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
|
||||
target.addImplementation(10000, 'code-editor', (accessor: ServicesAccessor, args: any) => {
|
||||
// Only if editor text focus (i.e. not if editor has widget focus).
|
||||
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
|
||||
if (focusedEditor && focusedEditor.hasTextFocus()) {
|
||||
@@ -290,7 +291,7 @@ abstract class EditorOrNativeTextInputCommand {
|
||||
});
|
||||
|
||||
// 2. handle case when focus is in some other `input` / `textarea`.
|
||||
target.addImplementation(1000, (accessor: ServicesAccessor, args: any) => {
|
||||
target.addImplementation(1000, 'generic-dom-input-textarea', (accessor: ServicesAccessor, args: any) => {
|
||||
// Only if focused on an element that allows for entering text
|
||||
const activeElement = <HTMLElement>document.activeElement;
|
||||
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
|
||||
@@ -301,7 +302,7 @@ abstract class EditorOrNativeTextInputCommand {
|
||||
});
|
||||
|
||||
// 3. (default) handle case when focus is somewhere else.
|
||||
target.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
|
||||
target.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
|
||||
// Redirecting to active editor
|
||||
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
|
||||
if (activeEditor) {
|
||||
@@ -1593,6 +1594,7 @@ export namespace CoreNavigationCommands {
|
||||
]
|
||||
);
|
||||
viewModel.revealPrimaryCursor(args.source, true);
|
||||
status(nls.localize('removedCursor', "Removed secondary cursors"));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1820,7 +1822,7 @@ export namespace CoreEditingCommands {
|
||||
}
|
||||
|
||||
public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
|
||||
const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection));
|
||||
const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection), viewModel.getCursorAutoClosedCharacters());
|
||||
if (shouldPushStackElementBefore) {
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ export class TextAreaHandler extends ViewPart {
|
||||
this.textArea.setAttribute('aria-haspopup', 'false');
|
||||
this.textArea.setAttribute('aria-autocomplete', 'both');
|
||||
|
||||
if (platform.isWeb && options.get(EditorOption.readOnly)) {
|
||||
if (options.get(EditorOption.domReadOnly)) {
|
||||
this.textArea.setAttribute('readonly', 'true');
|
||||
}
|
||||
|
||||
@@ -416,9 +416,8 @@ export class TextAreaHandler extends ViewPart {
|
||||
this._accessibilitySupport = options.get(EditorOption.accessibilitySupport);
|
||||
const accessibilityPageSize = options.get(EditorOption.accessibilityPageSize);
|
||||
if (this._accessibilitySupport === AccessibilitySupport.Enabled && accessibilityPageSize === EditorOptions.accessibilityPageSize.defaultValue) {
|
||||
// If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 100 for a better experience
|
||||
// If we put more than 100 lines the nvda can not handle this https://github.com/microsoft/vscode/issues/89717
|
||||
this._accessibilityPageSize = 100;
|
||||
// If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 1000 for a better experience
|
||||
this._accessibilityPageSize = 1000;
|
||||
} else {
|
||||
this._accessibilityPageSize = accessibilityPageSize;
|
||||
}
|
||||
@@ -441,8 +440,8 @@ export class TextAreaHandler extends ViewPart {
|
||||
this.textArea.setAttribute('aria-label', this._getAriaLabel(options));
|
||||
this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex)));
|
||||
|
||||
if (platform.isWeb && e.hasChanged(EditorOption.readOnly)) {
|
||||
if (options.get(EditorOption.readOnly)) {
|
||||
if (e.hasChanged(EditorOption.domReadOnly)) {
|
||||
if (options.get(EditorOption.domReadOnly)) {
|
||||
this.textArea.setAttribute('readonly', 'true');
|
||||
} else {
|
||||
this.textArea.removeAttribute('readonly');
|
||||
|
||||
@@ -209,26 +209,30 @@ export class TextAreaInput extends Disposable {
|
||||
|
||||
if (
|
||||
platform.isMacintosh
|
||||
&& lastKeyDown
|
||||
&& lastKeyDown.equals(KeyCode.KEY_IN_COMPOSITION)
|
||||
&& this._textAreaState.selectionStart === this._textAreaState.selectionEnd
|
||||
&& this._textAreaState.selectionStart > 0
|
||||
&& this._textAreaState.value.substr(this._textAreaState.selectionStart - 1, 1) === e.data
|
||||
&& (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft')
|
||||
) {
|
||||
// Handling long press case on macOS + arrow key => pretend the character was selected
|
||||
if (_debugComposition) {
|
||||
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
|
||||
}
|
||||
this._textAreaState = new TextAreaState(
|
||||
this._textAreaState.value,
|
||||
this._textAreaState.selectionStart - 1,
|
||||
this._textAreaState.selectionEnd,
|
||||
this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null,
|
||||
this._textAreaState.selectionEndPosition
|
||||
const isArrowKey = (
|
||||
lastKeyDown && lastKeyDown.equals(KeyCode.KEY_IN_COMPOSITION)
|
||||
&& (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft')
|
||||
);
|
||||
this._onCompositionStart.fire({ revealDeltaColumns: -1 });
|
||||
return;
|
||||
if (isArrowKey || browser.isFirefox) {
|
||||
// Handling long press case on Chromium/Safari macOS + arrow key => pretend the character was selected
|
||||
// or long press case on Firefox on macOS
|
||||
if (_debugComposition) {
|
||||
console.log(`[compositionstart] Handling long press case on macOS + arrow key or Firefox`, e);
|
||||
}
|
||||
this._textAreaState = new TextAreaState(
|
||||
this._textAreaState.value,
|
||||
this._textAreaState.selectionStart - 1,
|
||||
this._textAreaState.selectionEnd,
|
||||
this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null,
|
||||
this._textAreaState.selectionEndPosition
|
||||
);
|
||||
this._onCompositionStart.fire({ revealDeltaColumns: -1 });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (browser.isAndroid) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { withNullAsUndefined, assertType } from 'vs/base/common/types';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
|
||||
export type ServicesAccessor = InstantiationServicesAccessor;
|
||||
@@ -149,20 +150,26 @@ export abstract class Command {
|
||||
*/
|
||||
export type CommandImplementation = (accessor: ServicesAccessor, args: unknown) => boolean | Promise<void>;
|
||||
|
||||
interface ICommandImplementationRegistration {
|
||||
priority: number;
|
||||
name: string;
|
||||
implementation: CommandImplementation;
|
||||
}
|
||||
|
||||
export class MultiCommand extends Command {
|
||||
|
||||
private readonly _implementations: [number, CommandImplementation][] = [];
|
||||
private readonly _implementations: ICommandImplementationRegistration[] = [];
|
||||
|
||||
/**
|
||||
* A higher priority gets to be looked at first
|
||||
*/
|
||||
public addImplementation(priority: number, implementation: CommandImplementation): IDisposable {
|
||||
this._implementations.push([priority, implementation]);
|
||||
this._implementations.sort((a, b) => b[0] - a[0]);
|
||||
public addImplementation(priority: number, name: string, implementation: CommandImplementation): IDisposable {
|
||||
this._implementations.push({ priority, name, implementation });
|
||||
this._implementations.sort((a, b) => b.priority - a.priority);
|
||||
return {
|
||||
dispose: () => {
|
||||
for (let i = 0; i < this._implementations.length; i++) {
|
||||
if (this._implementations[i][1] === implementation) {
|
||||
if (this._implementations[i].implementation === implementation) {
|
||||
this._implementations.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
@@ -172,9 +179,11 @@ export class MultiCommand extends Command {
|
||||
}
|
||||
|
||||
public runCommand(accessor: ServicesAccessor, args: any): void | Promise<void> {
|
||||
const logService = accessor.get(ILogService);
|
||||
for (const impl of this._implementations) {
|
||||
const result = impl[1](accessor, args);
|
||||
const result = impl.implementation(accessor, args);
|
||||
if (result) {
|
||||
logService.trace(`Command '${this.id}' was handled by '${impl.name}'.`);
|
||||
if (typeof result === 'boolean') {
|
||||
return;
|
||||
}
|
||||
@@ -339,13 +348,13 @@ export abstract class EditorAction extends EditorCommand {
|
||||
public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class MultiEditorAction extends EditorAction {
|
||||
export class MultiEditorAction extends EditorAction {
|
||||
|
||||
private readonly _implementations: [number, CommandImplementation][] = [];
|
||||
|
||||
constructor(opts: IActionOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* A higher priority gets to be looked at first
|
||||
*/
|
||||
public addImplementation(priority: number, implementation: CommandImplementation): IDisposable {
|
||||
this._implementations.push([priority, implementation]);
|
||||
this._implementations.sort((a, b) => b[0] - a[0]);
|
||||
@@ -361,20 +370,18 @@ export abstract class MultiEditorAction extends EditorAction {
|
||||
};
|
||||
}
|
||||
|
||||
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void> {
|
||||
this.reportTelemetry(accessor, editor);
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void> {
|
||||
for (const impl of this._implementations) {
|
||||
if (impl[1](accessor, args)) {
|
||||
return;
|
||||
const result = impl[1](accessor, args);
|
||||
if (result) {
|
||||
if (typeof result === 'boolean') {
|
||||
return;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return this.run(accessor, editor, args || {});
|
||||
}
|
||||
|
||||
public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void>;
|
||||
|
||||
}
|
||||
|
||||
//#endregion EditorAction
|
||||
|
||||
@@ -88,7 +88,10 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
|
||||
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
|
||||
private readonly _themeService: IThemeService;
|
||||
|
||||
constructor(@IThemeService themeService: IThemeService, styleSheet: GlobalStyleSheet | null = null) {
|
||||
constructor(
|
||||
styleSheet: GlobalStyleSheet | null,
|
||||
@IThemeService themeService: IThemeService,
|
||||
) {
|
||||
super();
|
||||
this._globalStyleSheet = styleSheet ? styleSheet : null;
|
||||
this._themeService = themeService;
|
||||
@@ -328,7 +331,7 @@ export class DecorationTypeOptionsProvider implements IModelDecorationOptionsPro
|
||||
}
|
||||
|
||||
|
||||
const _CSS_MAP: { [prop: string]: string; } = {
|
||||
export const _CSS_MAP: { [prop: string]: string; } = {
|
||||
color: 'color:{0} !important;',
|
||||
opacity: 'opacity:{0};',
|
||||
backgroundColor: 'background-color:{0};',
|
||||
|
||||
@@ -166,7 +166,7 @@ export class OpenerService implements IOpenerService {
|
||||
// check with contributed validators
|
||||
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
|
||||
// validate against the original URI that this URI resolves to, if one exists
|
||||
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? targetURI;
|
||||
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target;
|
||||
for (const validator of this._validators) {
|
||||
if (!(await validator.shouldOpen(validationTarget))) {
|
||||
return false;
|
||||
|
||||
@@ -106,7 +106,7 @@ export class View extends ViewEventHandler {
|
||||
|
||||
// The view context is passed on to most classes (basically to reduce param. counts in ctors)
|
||||
this._context = new ViewContext(configuration, themeService.getColorTheme(), model);
|
||||
this._configPixelRatio = this._configPixelRatio = this._context.configuration.options.get(EditorOption.pixelRatio);
|
||||
this._configPixelRatio = this._context.configuration.options.get(EditorOption.pixelRatio);
|
||||
|
||||
// Ensure the view is the first event handler in order to update the layout
|
||||
this._context.addEventHandler(this);
|
||||
|
||||
@@ -121,9 +121,9 @@ export class RangeUtil {
|
||||
startChildIndex = Math.min(max, Math.max(min, startChildIndex));
|
||||
endChildIndex = Math.min(max, Math.max(min, endChildIndex));
|
||||
|
||||
if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0) {
|
||||
if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0 && !domNode.children[startChildIndex].firstChild) {
|
||||
// We must find the position at the beginning of a <span>
|
||||
// To cover cases of empty <span>s, aboid using a range and use the <span>'s bounding box
|
||||
// To cover cases of empty <span>s, avoid using a range and use the <span>'s bounding box
|
||||
const clientRects = domNode.children[startChildIndex].getClientRects();
|
||||
return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft);
|
||||
}
|
||||
|
||||
@@ -509,7 +509,7 @@ const editorConfiguration: IConfigurationNode = {
|
||||
nls.localize('wordBasedSuggestionsMode.matchingDocuments', 'Suggest words from all open documents of the same language.'),
|
||||
nls.localize('wordBasedSuggestionsMode.allDocuments', 'Suggest words from all open documents.')
|
||||
],
|
||||
description: nls.localize('wordBasedSuggestionsMode', "Controls from what documents word based completions are computed.")
|
||||
description: nls.localize('wordBasedSuggestionsMode', "Controls from which documents word based completions are computed.")
|
||||
},
|
||||
'editor.semanticHighlighting.enabled': {
|
||||
enum: [true, false, 'configuredByTheme'],
|
||||
|
||||
@@ -28,7 +28,7 @@ export type EditorAutoSurroundStrategy = 'languageDefined' | 'quotes' | 'bracket
|
||||
/**
|
||||
* Configuration options for typing over closing quotes or brackets
|
||||
*/
|
||||
export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never';
|
||||
export type EditorAutoClosingEditStrategy = 'always' | 'auto' | 'never';
|
||||
|
||||
/**
|
||||
* Configuration options for auto indentation in the editor
|
||||
@@ -139,10 +139,15 @@ export interface IEditorOptions {
|
||||
*/
|
||||
extraEditorClassName?: string;
|
||||
/**
|
||||
* Should the editor be read only.
|
||||
* Should the editor be read only. See also `domReadOnly`.
|
||||
* Defaults to false.
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/**
|
||||
* Should the textarea used for input use the DOM `readonly` attribute.
|
||||
* Defaults to false.
|
||||
*/
|
||||
domReadOnly?: boolean;
|
||||
/**
|
||||
* Enable linked editing.
|
||||
* Defaults to false.
|
||||
@@ -413,10 +418,14 @@ export interface IEditorOptions {
|
||||
* Defaults to language defined behavior.
|
||||
*/
|
||||
autoClosingQuotes?: EditorAutoClosingStrategy;
|
||||
/**
|
||||
* Options for pressing backspace near quotes or bracket pairs.
|
||||
*/
|
||||
autoClosingDelete?: EditorAutoClosingEditStrategy;
|
||||
/**
|
||||
* Options for typing over closing quotes or brackets.
|
||||
*/
|
||||
autoClosingOvertype?: EditorAutoClosingOvertypeStrategy;
|
||||
autoClosingOvertype?: EditorAutoClosingEditStrategy;
|
||||
/**
|
||||
* Options for auto surrounding.
|
||||
* Defaults to always allowing auto surrounding.
|
||||
@@ -1393,8 +1402,8 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
enum: ['never', 'always', 'multiline'],
|
||||
default: defaults.autoFindInSelection,
|
||||
enumDescriptions: [
|
||||
nls.localize('editor.find.autoFindInSelection.never', 'Never turn on Find in selection automatically (default)'),
|
||||
nls.localize('editor.find.autoFindInSelection.always', 'Always turn on Find in selection automatically'),
|
||||
nls.localize('editor.find.autoFindInSelection.never', 'Never turn on Find in selection automatically (default).'),
|
||||
nls.localize('editor.find.autoFindInSelection.always', 'Always turn on Find in selection automatically.'),
|
||||
nls.localize('editor.find.autoFindInSelection.multiline', 'Turn on Find in selection automatically when multiple lines of content are selected.')
|
||||
],
|
||||
description: nls.localize('find.autoFindInSelection', "Controls the condition for turning on find in selection automatically.")
|
||||
@@ -3140,7 +3149,7 @@ export interface ISuggestOptions {
|
||||
*/
|
||||
snippetsPreventQuickSuggestions?: boolean;
|
||||
/**
|
||||
* Favours words that appear close to the cursor.
|
||||
* Favors words that appear close to the cursor.
|
||||
*/
|
||||
localityBonus?: boolean;
|
||||
/**
|
||||
@@ -3332,7 +3341,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
'editor.suggest.localityBonus': {
|
||||
type: 'boolean',
|
||||
default: defaults.localityBonus,
|
||||
description: nls.localize('suggest.localityBonus', "Controls whether sorting favours words that appear close to the cursor.")
|
||||
description: nls.localize('suggest.localityBonus', "Controls whether sorting favors words that appear close to the cursor.")
|
||||
},
|
||||
'editor.suggest.shareSuggestSelections': {
|
||||
type: 'boolean',
|
||||
@@ -3725,6 +3734,7 @@ export const enum EditorOption {
|
||||
accessibilityPageSize,
|
||||
ariaLabel,
|
||||
autoClosingBrackets,
|
||||
autoClosingDelete,
|
||||
autoClosingOvertype,
|
||||
autoClosingQuotes,
|
||||
autoIndent,
|
||||
@@ -3746,6 +3756,7 @@ export const enum EditorOption {
|
||||
cursorWidth,
|
||||
disableLayerHinting,
|
||||
disableMonospaceOptimizations,
|
||||
domReadOnly,
|
||||
dragAndDrop,
|
||||
emptySelectionClipboard,
|
||||
extraEditorClassName,
|
||||
@@ -3883,7 +3894,10 @@ export const EditorOptions = {
|
||||
)),
|
||||
accessibilitySupport: register(new EditorAccessibilitySupport()),
|
||||
accessibilityPageSize: register(new EditorIntOption(EditorOption.accessibilityPageSize, 'accessibilityPageSize', 10, 1, Constants.MAX_SAFE_SMALL_INTEGER,
|
||||
{ description: nls.localize('accessibilityPageSize', "Controls the number of lines in the editor that can be read out by a screen reader. Warning: this has a performance implication for numbers larger than the default.") })),
|
||||
{
|
||||
description: nls.localize('accessibilityPageSize', "Controls the number of lines in the editor that can be read out by a screen reader at once. When we detect a screen reader we automatically set the default to be 2000. Warning: this has a performance implication for numbers larger than the default."),
|
||||
deprecationMessage: nls.localize('accessibilityPageSize.deprecated', "This setting is deprecated, editor will automatically choose the accessibility page size when we detect a screen reader. 2000 lines will be the new default.")
|
||||
})),
|
||||
ariaLabel: register(new EditorStringOption(
|
||||
EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content")
|
||||
)),
|
||||
@@ -3901,6 +3915,19 @@ export const EditorOptions = {
|
||||
description: nls.localize('autoClosingBrackets', "Controls whether the editor should automatically close brackets after the user adds an opening bracket.")
|
||||
}
|
||||
)),
|
||||
autoClosingDelete: register(new EditorStringEnumOption(
|
||||
EditorOption.autoClosingDelete, 'autoClosingDelete',
|
||||
'auto' as 'always' | 'auto' | 'never',
|
||||
['always', 'auto', 'never'] as const,
|
||||
{
|
||||
enumDescriptions: [
|
||||
'',
|
||||
nls.localize('editor.autoClosingDelete.auto', "Remove adjacent closing quotes or brackets only if they were automatically inserted."),
|
||||
'',
|
||||
],
|
||||
description: nls.localize('autoClosingDelete', "Controls whether the editor should remove adjacent closing quotes or brackets when deleting.")
|
||||
}
|
||||
)),
|
||||
autoClosingOvertype: register(new EditorStringEnumOption(
|
||||
EditorOption.autoClosingOvertype, 'autoClosingOvertype',
|
||||
'auto' as 'always' | 'auto' | 'never',
|
||||
@@ -3963,7 +3990,7 @@ export const EditorOptions = {
|
||||
)),
|
||||
stickyTabStops: register(new EditorBooleanOption(
|
||||
EditorOption.stickyTabStops, 'stickyTabStops', false,
|
||||
{ description: nls.localize('stickyTabStops', "Emulate selection behaviour of tab characters when using spaces for indentation. Selection will stick to tab stops.") }
|
||||
{ description: nls.localize('stickyTabStops', "Emulate selection behavior of tab characters when using spaces for indentation. Selection will stick to tab stops.") }
|
||||
)),
|
||||
codeLens: register(new EditorBooleanOption(
|
||||
EditorOption.codeLens, 'codeLens', true,
|
||||
@@ -4042,6 +4069,9 @@ export const EditorOptions = {
|
||||
disableMonospaceOptimizations: register(new EditorBooleanOption(
|
||||
EditorOption.disableMonospaceOptimizations, 'disableMonospaceOptimizations', false
|
||||
)),
|
||||
domReadOnly: register(new EditorBooleanOption(
|
||||
EditorOption.domReadOnly, 'domReadOnly', false,
|
||||
)),
|
||||
dragAndDrop: register(new EditorBooleanOption(
|
||||
EditorOption.dragAndDrop, 'dragAndDrop', true,
|
||||
{ description: nls.localize('dragAndDrop', "Controls whether the editor should allow moving selections via drag and drop.") }
|
||||
@@ -4264,7 +4294,7 @@ export const EditorOptions = {
|
||||
)),
|
||||
renderLineHighlightOnlyWhenFocus: register(new EditorBooleanOption(
|
||||
EditorOption.renderLineHighlightOnlyWhenFocus, 'renderLineHighlightOnlyWhenFocus', false,
|
||||
{ description: nls.localize('renderLineHighlightOnlyWhenFocus', "Controls if the editor should render the current line highlight only when the editor is focused") }
|
||||
{ description: nls.localize('renderLineHighlightOnlyWhenFocus', "Controls if the editor should render the current line highlight only when the editor is focused.") }
|
||||
)),
|
||||
renderValidationDecorations: register(new EditorStringEnumOption(
|
||||
EditorOption.renderValidationDecorations, 'renderValidationDecorations',
|
||||
@@ -4280,7 +4310,7 @@ export const EditorOptions = {
|
||||
'',
|
||||
nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."),
|
||||
nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."),
|
||||
nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters"),
|
||||
nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters."),
|
||||
''
|
||||
],
|
||||
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.")
|
||||
|
||||
@@ -620,6 +620,10 @@ export class Cursor extends Disposable {
|
||||
this._isDoingComposition = isDoingComposition;
|
||||
}
|
||||
|
||||
public getAutoClosedCharacters(): Range[] {
|
||||
return AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
|
||||
}
|
||||
|
||||
public startComposition(eventsCollector: ViewModelEventsCollector): void {
|
||||
this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
|
||||
}
|
||||
@@ -628,8 +632,7 @@ export class Cursor extends Disposable {
|
||||
this._executeEdit(() => {
|
||||
if (source === 'keyboard') {
|
||||
// composition finishes, let's check if we need to auto complete if necessary.
|
||||
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
|
||||
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
|
||||
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), this.getAutoClosedCharacters()));
|
||||
this._selectionsWhenCompositionStarted = null;
|
||||
}
|
||||
}, eventsCollector, source);
|
||||
@@ -647,8 +650,7 @@ export class Cursor extends Disposable {
|
||||
const chr = text.substr(offset, charLength);
|
||||
|
||||
// Here we must interpret each typed character individually
|
||||
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
|
||||
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr));
|
||||
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), this.getAutoClosedCharacters(), chr));
|
||||
|
||||
offset += charLength;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class ColumnSelection {
|
||||
|
||||
public static columnSelectLeft(config: CursorConfiguration, model: ICursorSimpleModel, prevColumnSelectData: IColumnSelectData): IColumnSelectResult {
|
||||
let toViewVisualColumn = prevColumnSelectData.toViewVisualColumn;
|
||||
if (toViewVisualColumn > 1) {
|
||||
if (toViewVisualColumn > 0) {
|
||||
toViewVisualColumn--;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingEditStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ISelection, Selection } from 'vs/editor/common/core/selection';
|
||||
@@ -73,7 +73,8 @@ export class CursorConfiguration {
|
||||
public readonly multiCursorPaste: 'spread' | 'full';
|
||||
public readonly autoClosingBrackets: EditorAutoClosingStrategy;
|
||||
public readonly autoClosingQuotes: EditorAutoClosingStrategy;
|
||||
public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy;
|
||||
public readonly autoClosingDelete: EditorAutoClosingEditStrategy;
|
||||
public readonly autoClosingOvertype: EditorAutoClosingEditStrategy;
|
||||
public readonly autoSurround: EditorAutoSurroundStrategy;
|
||||
public readonly autoIndent: EditorAutoIndentStrategy;
|
||||
public readonly autoClosingPairs: AutoClosingPairs;
|
||||
@@ -92,6 +93,7 @@ export class CursorConfiguration {
|
||||
|| e.hasChanged(EditorOption.multiCursorPaste)
|
||||
|| e.hasChanged(EditorOption.autoClosingBrackets)
|
||||
|| e.hasChanged(EditorOption.autoClosingQuotes)
|
||||
|| e.hasChanged(EditorOption.autoClosingDelete)
|
||||
|| e.hasChanged(EditorOption.autoClosingOvertype)
|
||||
|| e.hasChanged(EditorOption.autoSurround)
|
||||
|| e.hasChanged(EditorOption.useTabStops)
|
||||
@@ -125,6 +127,7 @@ export class CursorConfiguration {
|
||||
this.multiCursorPaste = options.get(EditorOption.multiCursorPaste);
|
||||
this.autoClosingBrackets = options.get(EditorOption.autoClosingBrackets);
|
||||
this.autoClosingQuotes = options.get(EditorOption.autoClosingQuotes);
|
||||
this.autoClosingDelete = options.get(EditorOption.autoClosingDelete);
|
||||
this.autoClosingOvertype = options.get(EditorOption.autoClosingOvertype);
|
||||
this.autoSurround = options.get(EditorOption.autoSurround);
|
||||
this.autoIndent = options.get(EditorOption.autoIndent);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
|
||||
import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon';
|
||||
import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -50,15 +50,20 @@ export class DeleteOperations {
|
||||
}
|
||||
|
||||
public static isAutoClosingPairDelete(
|
||||
autoClosingDelete: EditorAutoClosingEditStrategy,
|
||||
autoClosingBrackets: EditorAutoClosingStrategy,
|
||||
autoClosingQuotes: EditorAutoClosingStrategy,
|
||||
autoClosingPairsOpen: Map<string, StandardAutoClosingPairConditional[]>,
|
||||
model: ICursorSimpleModel,
|
||||
selections: Selection[]
|
||||
selections: Selection[],
|
||||
autoClosedCharacters: Range[]
|
||||
): boolean {
|
||||
if (autoClosingBrackets === 'never' && autoClosingQuotes === 'never') {
|
||||
return false;
|
||||
}
|
||||
if (autoClosingDelete === 'never') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0, len = selections.length; i < len; i++) {
|
||||
const selection = selections[i];
|
||||
@@ -100,6 +105,21 @@ export class DeleteOperations {
|
||||
if (!foundAutoClosingPair) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must delete the pair only if it was automatically inserted by the editor
|
||||
if (autoClosingDelete === 'auto') {
|
||||
let found = false;
|
||||
for (let j = 0, lenJ = autoClosedCharacters.length; j < lenJ; j++) {
|
||||
const autoClosedCharacter = autoClosedCharacters[j];
|
||||
if (position.lineNumber === autoClosedCharacter.startLineNumber && position.column === autoClosedCharacter.startColumn) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -120,9 +140,9 @@ export class DeleteOperations {
|
||||
return [true, commands];
|
||||
}
|
||||
|
||||
public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array<ICommand | null>] {
|
||||
public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], autoClosedCharacters: Range[]): [boolean, Array<ICommand | null>] {
|
||||
|
||||
if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections)) {
|
||||
if (this.isAutoClosingPairDelete(config.autoClosingDelete, config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections, autoClosedCharacters)) {
|
||||
return this._runAutoClosingPairDelete(config, model, selections);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon';
|
||||
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
|
||||
import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
|
||||
@@ -52,9 +52,11 @@ export interface DeleteWordContext {
|
||||
model: ITextModel;
|
||||
selection: Selection;
|
||||
whitespaceHeuristics: boolean;
|
||||
autoClosingDelete: EditorAutoClosingEditStrategy;
|
||||
autoClosingBrackets: EditorAutoClosingStrategy;
|
||||
autoClosingQuotes: EditorAutoClosingStrategy;
|
||||
autoClosingPairs: AutoClosingPairs;
|
||||
autoClosedCharacters: Range[];
|
||||
}
|
||||
|
||||
export class WordOperations {
|
||||
@@ -384,7 +386,7 @@ export class WordOperations {
|
||||
return selection;
|
||||
}
|
||||
|
||||
if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection])) {
|
||||
if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingDelete, ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection], ctx.autoClosedCharacters)) {
|
||||
const position = ctx.selection.getPosition();
|
||||
return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,26 @@ export interface IStringBuilder {
|
||||
appendASCIIString(str: string): void;
|
||||
}
|
||||
|
||||
let _utf16LE_TextDecoder: TextDecoder | null;
|
||||
function getUTF16LE_TextDecoder(): TextDecoder {
|
||||
if (!_utf16LE_TextDecoder) {
|
||||
_utf16LE_TextDecoder = new TextDecoder('UTF-16LE');
|
||||
}
|
||||
return _utf16LE_TextDecoder;
|
||||
}
|
||||
|
||||
let _utf16BE_TextDecoder: TextDecoder | null;
|
||||
function getUTF16BE_TextDecoder(): TextDecoder {
|
||||
if (!_utf16BE_TextDecoder) {
|
||||
_utf16BE_TextDecoder = new TextDecoder('UTF-16BE');
|
||||
}
|
||||
return _utf16BE_TextDecoder;
|
||||
}
|
||||
|
||||
let _platformTextDecoder: TextDecoder | null;
|
||||
export function getPlatformTextDecoder(): TextDecoder {
|
||||
if (!_platformTextDecoder) {
|
||||
_platformTextDecoder = new TextDecoder(platform.isLittleEndian() ? 'UTF-16LE' : 'UTF-16BE');
|
||||
_platformTextDecoder = platform.isLittleEndian() ? getUTF16LE_TextDecoder() : getUTF16BE_TextDecoder();
|
||||
}
|
||||
return _platformTextDecoder;
|
||||
}
|
||||
@@ -45,7 +61,14 @@ if (hasTextDecoder) {
|
||||
|
||||
function standardDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string {
|
||||
const view = new Uint16Array(source.buffer, offset, len);
|
||||
return getPlatformTextDecoder().decode(view);
|
||||
if (len > 0 && (view[0] === 0xFEFF || view[0] === 0xFFFE)) {
|
||||
// UTF16 sometimes starts with a BOM https://de.wikipedia.org/wiki/Byte_Order_Mark
|
||||
// It looks like TextDecoder.decode will eat up a leading BOM (0xFEFF or 0xFFFE)
|
||||
// We don't want that behavior because we know the string is UTF16LE and the BOM should be maintained
|
||||
// So we use the manual decoder
|
||||
return compatDecodeUTF16LE(source, offset, len);
|
||||
}
|
||||
return getUTF16LE_TextDecoder().decode(view);
|
||||
}
|
||||
|
||||
function compatDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string {
|
||||
|
||||
@@ -19,7 +19,7 @@ function uriGetComparisonKey(resource: URI): string {
|
||||
return resource.toString();
|
||||
}
|
||||
|
||||
class SingleModelEditStackData {
|
||||
export class SingleModelEditStackData {
|
||||
|
||||
public static create(model: ITextModel, beforeCursorState: Selection[] | null): SingleModelEditStackData {
|
||||
const alternativeVersionId = model.getAlternativeVersionId();
|
||||
|
||||
@@ -881,55 +881,49 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
* Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc.
|
||||
* Will try to not allocate if possible.
|
||||
*/
|
||||
private _validateRangeRelaxedNoAllocations(range: IRange): Range {
|
||||
public _validateRangeRelaxedNoAllocations(range: IRange): Range {
|
||||
const linesCount = this._buffer.getLineCount();
|
||||
|
||||
const initialStartLineNumber = range.startLineNumber;
|
||||
const initialStartColumn = range.startColumn;
|
||||
let startLineNumber: number;
|
||||
let startColumn: number;
|
||||
let startLineNumber = Math.floor((typeof initialStartLineNumber === 'number' && !isNaN(initialStartLineNumber)) ? initialStartLineNumber : 1);
|
||||
let startColumn = Math.floor((typeof initialStartColumn === 'number' && !isNaN(initialStartColumn)) ? initialStartColumn : 1);
|
||||
|
||||
if (initialStartLineNumber < 1) {
|
||||
if (startLineNumber < 1) {
|
||||
startLineNumber = 1;
|
||||
startColumn = 1;
|
||||
} else if (initialStartLineNumber > linesCount) {
|
||||
} else if (startLineNumber > linesCount) {
|
||||
startLineNumber = linesCount;
|
||||
startColumn = this.getLineMaxColumn(startLineNumber);
|
||||
} else {
|
||||
startLineNumber = initialStartLineNumber | 0;
|
||||
if (initialStartColumn <= 1) {
|
||||
if (startColumn <= 1) {
|
||||
startColumn = 1;
|
||||
} else {
|
||||
const maxColumn = this.getLineMaxColumn(startLineNumber);
|
||||
if (initialStartColumn >= maxColumn) {
|
||||
if (startColumn >= maxColumn) {
|
||||
startColumn = maxColumn;
|
||||
} else {
|
||||
startColumn = initialStartColumn | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initialEndLineNumber = range.endLineNumber;
|
||||
const initialEndColumn = range.endColumn;
|
||||
let endLineNumber: number;
|
||||
let endColumn: number;
|
||||
let endLineNumber = Math.floor((typeof initialEndLineNumber === 'number' && !isNaN(initialEndLineNumber)) ? initialEndLineNumber : 1);
|
||||
let endColumn = Math.floor((typeof initialEndColumn === 'number' && !isNaN(initialEndColumn)) ? initialEndColumn : 1);
|
||||
|
||||
if (initialEndLineNumber < 1) {
|
||||
if (endLineNumber < 1) {
|
||||
endLineNumber = 1;
|
||||
endColumn = 1;
|
||||
} else if (initialEndLineNumber > linesCount) {
|
||||
} else if (endLineNumber > linesCount) {
|
||||
endLineNumber = linesCount;
|
||||
endColumn = this.getLineMaxColumn(endLineNumber);
|
||||
} else {
|
||||
endLineNumber = initialEndLineNumber | 0;
|
||||
if (initialEndColumn <= 1) {
|
||||
if (endColumn <= 1) {
|
||||
endColumn = 1;
|
||||
} else {
|
||||
const maxColumn = this.getLineMaxColumn(endLineNumber);
|
||||
if (initialEndColumn >= maxColumn) {
|
||||
if (endColumn >= maxColumn) {
|
||||
endColumn = maxColumn;
|
||||
} else {
|
||||
endColumn = initialEndColumn | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,11 +297,11 @@ export interface EvaluatableExpressionProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* An open ended information bag passed to the inline value provider.
|
||||
* A minimal context containes just the document location where the debugger has stopped.
|
||||
* A value-object that contains contextual information when requesting inline values from a InlineValuesProvider.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValueContext {
|
||||
frameId: number;
|
||||
stoppedLocation: Range;
|
||||
}
|
||||
|
||||
@@ -699,8 +699,8 @@ export interface CodeAction {
|
||||
* @internal
|
||||
*/
|
||||
export const enum CodeActionTriggerType {
|
||||
Auto = 1,
|
||||
Manual = 2,
|
||||
Invoke = 1,
|
||||
Auto = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1683,33 +1683,6 @@ export interface CommentThreadChangedEvent {
|
||||
readonly changed: CommentThread[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IWebviewPortMapping {
|
||||
webviewPort: number;
|
||||
extensionHostPort: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IWebviewOptions {
|
||||
readonly enableScripts?: boolean;
|
||||
readonly enableCommandUris?: boolean;
|
||||
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
|
||||
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IWebviewPanelOptions {
|
||||
readonly enableFindWidget?: boolean;
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface CodeLens {
|
||||
range: IRange;
|
||||
id?: string;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { stringDiff } from 'vs/base/common/diff/diff';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { globals } from 'vs/base/common/platform';
|
||||
@@ -455,7 +454,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
const result: TextEdit[] = [];
|
||||
let lastEol: EndOfLineSequence | undefined = undefined;
|
||||
|
||||
edits = mergeSort(edits, (a, b) => {
|
||||
edits = edits.slice(0).sort((a, b) => {
|
||||
if (a.range && b.range) {
|
||||
return Range.compareRangesUsingStarts(a.range, b.range);
|
||||
}
|
||||
|
||||
@@ -741,6 +741,19 @@ export class ModelSemanticColoring extends Disposable {
|
||||
this._fetchDocumentSemanticTokens.schedule();
|
||||
}
|
||||
}));
|
||||
this._register(this._model.onDidChangeLanguage(() => {
|
||||
// clear any outstanding state
|
||||
if (this._currentDocumentResponse) {
|
||||
this._currentDocumentResponse.dispose();
|
||||
this._currentDocumentResponse = null;
|
||||
}
|
||||
if (this._currentDocumentRequestCancellationTokenSource) {
|
||||
this._currentDocumentRequestCancellationTokenSource.cancel();
|
||||
this._currentDocumentRequestCancellationTokenSource = null;
|
||||
}
|
||||
this._setDocumentSemanticTokens(null, null, null, []);
|
||||
this._fetchDocumentSemanticTokens.schedule(0);
|
||||
}));
|
||||
const bindDocumentChangeListeners = () => {
|
||||
dispose(this._documentProvidersChangeListeners);
|
||||
this._documentProvidersChangeListeners = [];
|
||||
|
||||
@@ -173,126 +173,128 @@ export enum EditorOption {
|
||||
accessibilityPageSize = 3,
|
||||
ariaLabel = 4,
|
||||
autoClosingBrackets = 5,
|
||||
autoClosingOvertype = 6,
|
||||
autoClosingQuotes = 7,
|
||||
autoIndent = 8,
|
||||
automaticLayout = 9,
|
||||
autoSurround = 10,
|
||||
codeLens = 11,
|
||||
codeLensFontFamily = 12,
|
||||
codeLensFontSize = 13,
|
||||
colorDecorators = 14,
|
||||
columnSelection = 15,
|
||||
comments = 16,
|
||||
contextmenu = 17,
|
||||
copyWithSyntaxHighlighting = 18,
|
||||
cursorBlinking = 19,
|
||||
cursorSmoothCaretAnimation = 20,
|
||||
cursorStyle = 21,
|
||||
cursorSurroundingLines = 22,
|
||||
cursorSurroundingLinesStyle = 23,
|
||||
cursorWidth = 24,
|
||||
disableLayerHinting = 25,
|
||||
disableMonospaceOptimizations = 26,
|
||||
dragAndDrop = 27,
|
||||
emptySelectionClipboard = 28,
|
||||
extraEditorClassName = 29,
|
||||
fastScrollSensitivity = 30,
|
||||
find = 31,
|
||||
fixedOverflowWidgets = 32,
|
||||
folding = 33,
|
||||
foldingStrategy = 34,
|
||||
foldingHighlight = 35,
|
||||
unfoldOnClickAfterEndOfLine = 36,
|
||||
fontFamily = 37,
|
||||
fontInfo = 38,
|
||||
fontLigatures = 39,
|
||||
fontSize = 40,
|
||||
fontWeight = 41,
|
||||
formatOnPaste = 42,
|
||||
formatOnType = 43,
|
||||
glyphMargin = 44,
|
||||
gotoLocation = 45,
|
||||
hideCursorInOverviewRuler = 46,
|
||||
highlightActiveIndentGuide = 47,
|
||||
hover = 48,
|
||||
inDiffEditor = 49,
|
||||
letterSpacing = 50,
|
||||
lightbulb = 51,
|
||||
lineDecorationsWidth = 52,
|
||||
lineHeight = 53,
|
||||
lineNumbers = 54,
|
||||
lineNumbersMinChars = 55,
|
||||
linkedEditing = 56,
|
||||
links = 57,
|
||||
matchBrackets = 58,
|
||||
minimap = 59,
|
||||
mouseStyle = 60,
|
||||
mouseWheelScrollSensitivity = 61,
|
||||
mouseWheelZoom = 62,
|
||||
multiCursorMergeOverlapping = 63,
|
||||
multiCursorModifier = 64,
|
||||
multiCursorPaste = 65,
|
||||
occurrencesHighlight = 66,
|
||||
overviewRulerBorder = 67,
|
||||
overviewRulerLanes = 68,
|
||||
padding = 69,
|
||||
parameterHints = 70,
|
||||
peekWidgetDefaultFocus = 71,
|
||||
definitionLinkOpensInPeek = 72,
|
||||
quickSuggestions = 73,
|
||||
quickSuggestionsDelay = 74,
|
||||
readOnly = 75,
|
||||
renameOnType = 76,
|
||||
renderControlCharacters = 77,
|
||||
renderIndentGuides = 78,
|
||||
renderFinalNewline = 79,
|
||||
renderLineHighlight = 80,
|
||||
renderLineHighlightOnlyWhenFocus = 81,
|
||||
renderValidationDecorations = 82,
|
||||
renderWhitespace = 83,
|
||||
revealHorizontalRightPadding = 84,
|
||||
roundedSelection = 85,
|
||||
rulers = 86,
|
||||
scrollbar = 87,
|
||||
scrollBeyondLastColumn = 88,
|
||||
scrollBeyondLastLine = 89,
|
||||
scrollPredominantAxis = 90,
|
||||
selectionClipboard = 91,
|
||||
selectionHighlight = 92,
|
||||
selectOnLineNumbers = 93,
|
||||
showFoldingControls = 94,
|
||||
showUnused = 95,
|
||||
snippetSuggestions = 96,
|
||||
smartSelect = 97,
|
||||
smoothScrolling = 98,
|
||||
stickyTabStops = 99,
|
||||
stopRenderingLineAfter = 100,
|
||||
suggest = 101,
|
||||
suggestFontSize = 102,
|
||||
suggestLineHeight = 103,
|
||||
suggestOnTriggerCharacters = 104,
|
||||
suggestSelection = 105,
|
||||
tabCompletion = 106,
|
||||
tabIndex = 107,
|
||||
unusualLineTerminators = 108,
|
||||
useTabStops = 109,
|
||||
wordSeparators = 110,
|
||||
wordWrap = 111,
|
||||
wordWrapBreakAfterCharacters = 112,
|
||||
wordWrapBreakBeforeCharacters = 113,
|
||||
wordWrapColumn = 114,
|
||||
wordWrapOverride1 = 115,
|
||||
wordWrapOverride2 = 116,
|
||||
wrappingIndent = 117,
|
||||
wrappingStrategy = 118,
|
||||
showDeprecated = 119,
|
||||
inlineHints = 120,
|
||||
editorClassName = 121,
|
||||
pixelRatio = 122,
|
||||
tabFocusMode = 123,
|
||||
layoutInfo = 124,
|
||||
wrappingInfo = 125
|
||||
autoClosingDelete = 6,
|
||||
autoClosingOvertype = 7,
|
||||
autoClosingQuotes = 8,
|
||||
autoIndent = 9,
|
||||
automaticLayout = 10,
|
||||
autoSurround = 11,
|
||||
codeLens = 12,
|
||||
codeLensFontFamily = 13,
|
||||
codeLensFontSize = 14,
|
||||
colorDecorators = 15,
|
||||
columnSelection = 16,
|
||||
comments = 17,
|
||||
contextmenu = 18,
|
||||
copyWithSyntaxHighlighting = 19,
|
||||
cursorBlinking = 20,
|
||||
cursorSmoothCaretAnimation = 21,
|
||||
cursorStyle = 22,
|
||||
cursorSurroundingLines = 23,
|
||||
cursorSurroundingLinesStyle = 24,
|
||||
cursorWidth = 25,
|
||||
disableLayerHinting = 26,
|
||||
disableMonospaceOptimizations = 27,
|
||||
domReadOnly = 28,
|
||||
dragAndDrop = 29,
|
||||
emptySelectionClipboard = 30,
|
||||
extraEditorClassName = 31,
|
||||
fastScrollSensitivity = 32,
|
||||
find = 33,
|
||||
fixedOverflowWidgets = 34,
|
||||
folding = 35,
|
||||
foldingStrategy = 36,
|
||||
foldingHighlight = 37,
|
||||
unfoldOnClickAfterEndOfLine = 38,
|
||||
fontFamily = 39,
|
||||
fontInfo = 40,
|
||||
fontLigatures = 41,
|
||||
fontSize = 42,
|
||||
fontWeight = 43,
|
||||
formatOnPaste = 44,
|
||||
formatOnType = 45,
|
||||
glyphMargin = 46,
|
||||
gotoLocation = 47,
|
||||
hideCursorInOverviewRuler = 48,
|
||||
highlightActiveIndentGuide = 49,
|
||||
hover = 50,
|
||||
inDiffEditor = 51,
|
||||
letterSpacing = 52,
|
||||
lightbulb = 53,
|
||||
lineDecorationsWidth = 54,
|
||||
lineHeight = 55,
|
||||
lineNumbers = 56,
|
||||
lineNumbersMinChars = 57,
|
||||
linkedEditing = 58,
|
||||
links = 59,
|
||||
matchBrackets = 60,
|
||||
minimap = 61,
|
||||
mouseStyle = 62,
|
||||
mouseWheelScrollSensitivity = 63,
|
||||
mouseWheelZoom = 64,
|
||||
multiCursorMergeOverlapping = 65,
|
||||
multiCursorModifier = 66,
|
||||
multiCursorPaste = 67,
|
||||
occurrencesHighlight = 68,
|
||||
overviewRulerBorder = 69,
|
||||
overviewRulerLanes = 70,
|
||||
padding = 71,
|
||||
parameterHints = 72,
|
||||
peekWidgetDefaultFocus = 73,
|
||||
definitionLinkOpensInPeek = 74,
|
||||
quickSuggestions = 75,
|
||||
quickSuggestionsDelay = 76,
|
||||
readOnly = 77,
|
||||
renameOnType = 78,
|
||||
renderControlCharacters = 79,
|
||||
renderIndentGuides = 80,
|
||||
renderFinalNewline = 81,
|
||||
renderLineHighlight = 82,
|
||||
renderLineHighlightOnlyWhenFocus = 83,
|
||||
renderValidationDecorations = 84,
|
||||
renderWhitespace = 85,
|
||||
revealHorizontalRightPadding = 86,
|
||||
roundedSelection = 87,
|
||||
rulers = 88,
|
||||
scrollbar = 89,
|
||||
scrollBeyondLastColumn = 90,
|
||||
scrollBeyondLastLine = 91,
|
||||
scrollPredominantAxis = 92,
|
||||
selectionClipboard = 93,
|
||||
selectionHighlight = 94,
|
||||
selectOnLineNumbers = 95,
|
||||
showFoldingControls = 96,
|
||||
showUnused = 97,
|
||||
snippetSuggestions = 98,
|
||||
smartSelect = 99,
|
||||
smoothScrolling = 100,
|
||||
stickyTabStops = 101,
|
||||
stopRenderingLineAfter = 102,
|
||||
suggest = 103,
|
||||
suggestFontSize = 104,
|
||||
suggestLineHeight = 105,
|
||||
suggestOnTriggerCharacters = 106,
|
||||
suggestSelection = 107,
|
||||
tabCompletion = 108,
|
||||
tabIndex = 109,
|
||||
unusualLineTerminators = 110,
|
||||
useTabStops = 111,
|
||||
wordSeparators = 112,
|
||||
wordWrap = 113,
|
||||
wordWrapBreakAfterCharacters = 114,
|
||||
wordWrapBreakBeforeCharacters = 115,
|
||||
wordWrapColumn = 116,
|
||||
wordWrapOverride1 = 117,
|
||||
wordWrapOverride2 = 118,
|
||||
wrappingIndent = 119,
|
||||
wrappingStrategy = 120,
|
||||
showDeprecated = 121,
|
||||
inlineHints = 122,
|
||||
editorClassName = 123,
|
||||
pixelRatio = 124,
|
||||
tabFocusMode = 125,
|
||||
layoutInfo = 126,
|
||||
wrappingInfo = 127
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -346,38 +346,48 @@ export class RenderLineOutput {
|
||||
export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): RenderLineOutput {
|
||||
if (input.lineContent.length === 0) {
|
||||
|
||||
let containsForeignElements = ForeignElementType.None;
|
||||
|
||||
let content: string = '<span><span></span></span>';
|
||||
|
||||
if (input.lineDecorations.length > 0) {
|
||||
// This line is empty, but it contains inline decorations
|
||||
const beforeClassNames: string[] = [];
|
||||
const afterClassNames: string[] = [];
|
||||
for (let i = 0, len = input.lineDecorations.length; i < len; i++) {
|
||||
const lineDecoration = input.lineDecorations[i];
|
||||
if (lineDecoration.type === InlineDecorationType.Before) {
|
||||
beforeClassNames.push(input.lineDecorations[i].className);
|
||||
containsForeignElements |= ForeignElementType.Before;
|
||||
}
|
||||
if (lineDecoration.type === InlineDecorationType.After) {
|
||||
afterClassNames.push(input.lineDecorations[i].className);
|
||||
containsForeignElements |= ForeignElementType.After;
|
||||
sb.appendASCIIString(`<span>`);
|
||||
|
||||
let beforeCount = 0;
|
||||
let afterCount = 0;
|
||||
let containsForeignElements = ForeignElementType.None;
|
||||
for (const lineDecoration of input.lineDecorations) {
|
||||
if (lineDecoration.type === InlineDecorationType.Before || lineDecoration.type === InlineDecorationType.After) {
|
||||
sb.appendASCIIString(`<span class="`);
|
||||
sb.appendASCIIString(lineDecoration.className);
|
||||
sb.appendASCIIString(`"></span>`);
|
||||
|
||||
if (lineDecoration.type === InlineDecorationType.Before) {
|
||||
containsForeignElements |= ForeignElementType.Before;
|
||||
beforeCount++;
|
||||
}
|
||||
if (lineDecoration.type === InlineDecorationType.After) {
|
||||
containsForeignElements |= ForeignElementType.After;
|
||||
afterCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (containsForeignElements !== ForeignElementType.None) {
|
||||
const beforeSpan = (beforeClassNames.length > 0 ? `<span class="${beforeClassNames.join(' ')}"></span>` : ``);
|
||||
const afterSpan = (afterClassNames.length > 0 ? `<span class="${afterClassNames.join(' ')}"></span>` : ``);
|
||||
content = `<span>${beforeSpan}${afterSpan}</span>`;
|
||||
}
|
||||
sb.appendASCIIString(`</span>`);
|
||||
|
||||
const characterMapping = new CharacterMapping(1, beforeCount + afterCount);
|
||||
characterMapping.setPartData(0, beforeCount, 0, 0);
|
||||
|
||||
return new RenderLineOutput(
|
||||
characterMapping,
|
||||
false,
|
||||
containsForeignElements
|
||||
);
|
||||
}
|
||||
|
||||
sb.appendASCIIString(content);
|
||||
// completely empty line
|
||||
sb.appendASCIIString('<span><span></span></span>');
|
||||
return new RenderLineOutput(
|
||||
new CharacterMapping(0, 0),
|
||||
false,
|
||||
containsForeignElements
|
||||
ForeignElementType.None
|
||||
);
|
||||
}
|
||||
|
||||
@@ -943,7 +953,12 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
|
||||
break;
|
||||
|
||||
case CharCode.Null:
|
||||
sb.appendASCIIString('�');
|
||||
if (renderControlCharacters) {
|
||||
// See https://unicode-table.com/en/blocks/control-pictures/
|
||||
sb.write1(9216);
|
||||
} else {
|
||||
sb.appendASCIIString('�');
|
||||
}
|
||||
break;
|
||||
|
||||
case CharCode.UTF8_BOM:
|
||||
@@ -957,8 +972,12 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
|
||||
if (strings.isFullWidthCharacter(charCode)) {
|
||||
charWidth++;
|
||||
}
|
||||
// See https://unicode-table.com/en/blocks/control-pictures/
|
||||
if (renderControlCharacters && charCode < 32) {
|
||||
sb.write1(9216 + charCode);
|
||||
} else if (renderControlCharacters && charCode === 127) {
|
||||
// DEL
|
||||
sb.write1(9249);
|
||||
} else {
|
||||
sb.write1(charCode);
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ export interface IViewModel extends ICursorSimpleModel {
|
||||
getCursorStates(): CursorState[];
|
||||
setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void;
|
||||
getCursorColumnSelectData(): IColumnSelectData;
|
||||
getCursorAutoClosedCharacters(): Range[];
|
||||
setCursorColumnSelectData(columnSelectData: IColumnSelectData): void;
|
||||
getPrevEditOperationType(): EditOperationType;
|
||||
setPrevEditOperationType(type: EditOperationType): void;
|
||||
|
||||
@@ -898,6 +898,9 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
public getCursorColumnSelectData(): IColumnSelectData {
|
||||
return this._cursor.getCursorColumnSelectData();
|
||||
}
|
||||
public getCursorAutoClosedCharacters(): Range[] {
|
||||
return this._cursor.getAutoClosedCharacters();
|
||||
}
|
||||
public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void {
|
||||
this._cursor.setCursorColumnSelectData(columnSelectData);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -94,6 +94,9 @@ export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({
|
||||
}]
|
||||
})) : undefined;
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { submenu: MenuId.MenubarCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: '2_ccp', order: 3 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 });
|
||||
|
||||
export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({
|
||||
id: 'editor.action.clipboardPasteAction',
|
||||
precondition: undefined,
|
||||
@@ -111,13 +114,13 @@ export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({
|
||||
menuId: MenuId.MenubarEditMenu,
|
||||
group: '2_ccp',
|
||||
title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
|
||||
order: 3
|
||||
order: 4
|
||||
}, {
|
||||
menuId: MenuId.EditorContext,
|
||||
group: CLIPBOARD_CONTEXT_MENU_GROUP,
|
||||
title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
|
||||
when: EditorContextKeys.writable,
|
||||
order: 3,
|
||||
order: 4,
|
||||
}, {
|
||||
menuId: MenuId.CommandPalette,
|
||||
group: '',
|
||||
@@ -166,7 +169,7 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
|
||||
}
|
||||
|
||||
// 1. handle case when focus is in editor.
|
||||
target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
|
||||
target.addImplementation(10000, 'code-editor', (accessor: ServicesAccessor, args: any) => {
|
||||
// Only if editor text focus (i.e. not if editor has widget focus).
|
||||
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
|
||||
if (focusedEditor && focusedEditor.hasTextFocus()) {
|
||||
@@ -183,7 +186,7 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
|
||||
});
|
||||
|
||||
// 2. (default) handle case when focus is somewhere else.
|
||||
target.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
|
||||
target.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
|
||||
document.execCommand(browserCommand);
|
||||
return true;
|
||||
});
|
||||
@@ -194,7 +197,7 @@ registerExecCommandImpl(CopyAction, 'copy');
|
||||
|
||||
if (PasteAction) {
|
||||
// 1. Paste: handle case when focus is in editor.
|
||||
PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
|
||||
PasteAction.addImplementation(10000, 'code-editor', (accessor: ServicesAccessor, args: any) => {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
|
||||
@@ -232,7 +235,7 @@ if (PasteAction) {
|
||||
});
|
||||
|
||||
// 2. Paste: (default) handle case when focus is somewhere else.
|
||||
PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
|
||||
PasteAction.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
|
||||
document.execCommand('paste');
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals, flatten, isNonEmptyArray, mergeSort, coalesce } from 'vs/base/common/arrays';
|
||||
import { equals, flatten, isNonEmptyArray, coalesce } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -87,7 +87,7 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet {
|
||||
) {
|
||||
super();
|
||||
this._register(disposables);
|
||||
this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
|
||||
this.allActions = [...actions].sort(ManagedCodeActionSet.codeActionsComparator);
|
||||
this.validActions = this.allActions.filter(({ action }) => !action.disabled);
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (a
|
||||
const codeActionSet = await getCodeActions(
|
||||
model,
|
||||
validatedRangeOrSelection,
|
||||
{ type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include } },
|
||||
{ type: modes.CodeActionTriggerType.Invoke, filter: { includeSourceActions: true, include } },
|
||||
Progress.None,
|
||||
CancellationToken.None);
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ export class QuickFixController extends Disposable implements IEditorContributio
|
||||
|
||||
MessageController.get(this._editor).closeMessage();
|
||||
const triggerPosition = this._editor.getPosition();
|
||||
this._trigger({ type: CodeActionTriggerType.Manual, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
|
||||
this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } });
|
||||
}
|
||||
|
||||
private _trigger(trigger: CodeActionTrigger) {
|
||||
|
||||
@@ -247,7 +247,7 @@ export class CodeActionModel extends Disposable {
|
||||
}
|
||||
|
||||
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token));
|
||||
if (trigger.trigger.type === CodeActionTriggerType.Manual) {
|
||||
if (trigger.trigger.type === CodeActionTriggerType.Invoke) {
|
||||
this._progressService?.showWhile(actions, 250);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export class CodeActionUi extends Disposable {
|
||||
|
||||
this._lightBulbWidget.getValue().update(actions, newState.trigger, newState.position);
|
||||
|
||||
if (newState.trigger.type === CodeActionTriggerType.Manual) {
|
||||
if (newState.trigger.type === CodeActionTriggerType.Invoke) {
|
||||
if (newState.trigger.filter?.include) { // Triggered for specific scope
|
||||
// Check to see if we want to auto apply.
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ suite('CodeAction', () => {
|
||||
new CodeActionItem(testData.tsLint.abc, provider)
|
||||
];
|
||||
|
||||
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
|
||||
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None);
|
||||
assert.equal(actions.length, 6);
|
||||
assert.deepEqual(actions, expected);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -59,7 +58,7 @@ export async function getCodeLensModel(model: ITextModel, token: CancellationTok
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
result.lenses = mergeSort(result.lenses, (a, b) => {
|
||||
result.lenses = result.lenses.sort((a, b) => {
|
||||
// sort by lineNumber, provider-rank, and column
|
||||
if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) {
|
||||
return -1;
|
||||
|
||||
@@ -90,10 +90,10 @@ class CodeLensContentWidget implements IContentWidget {
|
||||
if (lens.command) {
|
||||
const title = renderLabelWithIcons(lens.command.title.trim());
|
||||
if (lens.command.id) {
|
||||
children.push(dom.$('a', { id: String(i) }, ...title));
|
||||
children.push(dom.$('a', { id: String(i), title: lens.command.tooltip }, ...title));
|
||||
this._commands.set(String(i), lens.command);
|
||||
} else {
|
||||
children.push(dom.$('span', undefined, ...title));
|
||||
children.push(dom.$('span', { title: lens.command.tooltip }, ...title));
|
||||
}
|
||||
if (i + 1 < lenses.length) {
|
||||
children.push(dom.$('span', undefined, '\u00a0|\u00a0'));
|
||||
|
||||
@@ -26,6 +26,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
|
||||
const SEARCH_STRING_MAX_LENGTH = 524288;
|
||||
|
||||
@@ -483,43 +484,44 @@ export class FindController extends CommonFindController implements IFindControl
|
||||
}
|
||||
}
|
||||
|
||||
export class StartFindAction extends MultiEditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: FIND_IDS.StartFindAction,
|
||||
label: nls.localize('startFindAction', "Find"),
|
||||
alias: 'Find',
|
||||
precondition: ContextKeyExpr.or(ContextKeyExpr.has('editorFocus'), ContextKeyExpr.has('editorIsOpen')),
|
||||
kbOpts: {
|
||||
kbExpr: null,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
menuOpts: {
|
||||
menuId: MenuId.MenubarEditMenu,
|
||||
group: '3_find',
|
||||
title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"),
|
||||
order: 1
|
||||
}
|
||||
});
|
||||
export const StartFindAction = registerMultiEditorAction(new MultiEditorAction({
|
||||
id: FIND_IDS.StartFindAction,
|
||||
label: nls.localize('startFindAction', "Find"),
|
||||
alias: 'Find',
|
||||
precondition: ContextKeyExpr.or(EditorContextKeys.focus, ContextKeyExpr.has('editorIsOpen')),
|
||||
kbOpts: {
|
||||
kbExpr: null,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
menuOpts: {
|
||||
menuId: MenuId.MenubarEditMenu,
|
||||
group: '3_find',
|
||||
title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"),
|
||||
order: 1
|
||||
}
|
||||
}));
|
||||
|
||||
public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
|
||||
let controller = CommonFindController.get(editor);
|
||||
if (controller) {
|
||||
await controller.start({
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none',
|
||||
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard,
|
||||
shouldFocus: FindStartFocusAction.FocusFindInput,
|
||||
shouldAnimate: true,
|
||||
updateSearchScope: false,
|
||||
loop: editor.getOption(EditorOption.find).loop
|
||||
});
|
||||
}
|
||||
StartFindAction.addImplementation(0, (accessor: ServicesAccessor, args: any): boolean | Promise<void> => {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor();
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const controller = CommonFindController.get(editor);
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
return controller.start({
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none',
|
||||
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard,
|
||||
shouldFocus: FindStartFocusAction.FocusFindInput,
|
||||
shouldAnimate: true,
|
||||
updateSearchScope: false,
|
||||
loop: editor.getOption(EditorOption.find).loop
|
||||
});
|
||||
});
|
||||
|
||||
export class StartFindWithSelectionAction extends EditorAction {
|
||||
|
||||
@@ -744,71 +746,66 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class StartFindReplaceAction extends MultiEditorAction {
|
||||
export const StartFindReplaceAction = registerMultiEditorAction(new MultiEditorAction({
|
||||
id: FIND_IDS.StartFindReplaceAction,
|
||||
label: nls.localize('startReplace', "Replace"),
|
||||
alias: 'Replace',
|
||||
precondition: ContextKeyExpr.or(EditorContextKeys.focus, ContextKeyExpr.has('editorIsOpen')),
|
||||
kbOpts: {
|
||||
kbExpr: null,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_H,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
menuOpts: {
|
||||
menuId: MenuId.MenubarEditMenu,
|
||||
group: '3_find',
|
||||
title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
|
||||
order: 2
|
||||
}
|
||||
}));
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: FIND_IDS.StartFindReplaceAction,
|
||||
label: nls.localize('startReplace', "Replace"),
|
||||
alias: 'Replace',
|
||||
precondition: ContextKeyExpr.or(ContextKeyExpr.has('editorFocus'), ContextKeyExpr.has('editorIsOpen')),
|
||||
kbOpts: {
|
||||
kbExpr: null,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_H,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
},
|
||||
menuOpts: {
|
||||
menuId: MenuId.MenubarEditMenu,
|
||||
group: '3_find',
|
||||
title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
|
||||
order: 2
|
||||
}
|
||||
});
|
||||
StartFindReplaceAction.addImplementation(0, (accessor: ServicesAccessor, args: any): boolean | Promise<void> => {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor();
|
||||
if (!editor || !editor.hasModel() || editor.getOption(EditorOption.readOnly)) {
|
||||
return false;
|
||||
}
|
||||
const controller = CommonFindController.get(editor);
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise<void> {
|
||||
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly)) {
|
||||
return;
|
||||
}
|
||||
const currentSelection = editor.getSelection();
|
||||
const findInputFocused = controller.isFindInputFocused();
|
||||
// we only seed search string from selection when the current selection is single line and not empty,
|
||||
// + the find input is not focused
|
||||
const seedSearchStringFromSelection = !currentSelection.isEmpty()
|
||||
&& currentSelection.startLineNumber === currentSelection.endLineNumber && editor.getOption(EditorOption.find).seedSearchStringFromSelection
|
||||
&& !findInputFocused;
|
||||
/*
|
||||
* if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input is still empty, so we should focus the Find Input instead of Replace Input.
|
||||
|
||||
let controller = CommonFindController.get(editor);
|
||||
let currentSelection = editor.getSelection();
|
||||
let findInputFocused = controller.isFindInputFocused();
|
||||
// we only seed search string from selection when the current selection is single line and not empty,
|
||||
// + the find input is not focused
|
||||
let seedSearchStringFromSelection = !currentSelection.isEmpty()
|
||||
&& currentSelection.startLineNumber === currentSelection.endLineNumber && editor.getOption(EditorOption.find).seedSearchStringFromSelection
|
||||
&& !findInputFocused;
|
||||
/*
|
||||
* if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input is still empty, so we should focus the Find Input instead of Replace Input.
|
||||
* findInputFocused true -> seedSearchStringFromSelection false, FocusReplaceInput
|
||||
* findInputFocused false, seedSearchStringFromSelection true FocusReplaceInput
|
||||
* findInputFocused false seedSearchStringFromSelection false FocusFindInput
|
||||
*/
|
||||
const shouldFocus = (findInputFocused || seedSearchStringFromSelection) ?
|
||||
FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput;
|
||||
|
||||
* findInputFocused true -> seedSearchStringFromSelection false, FocusReplaceInput
|
||||
* findInputFocused false, seedSearchStringFromSelection true FocusReplaceInput
|
||||
* findInputFocused false seedSearchStringFromSelection false FocusFindInput
|
||||
*/
|
||||
let shouldFocus = (findInputFocused || seedSearchStringFromSelection) ?
|
||||
FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput;
|
||||
|
||||
|
||||
if (controller) {
|
||||
await controller.start({
|
||||
forceRevealReplace: true,
|
||||
seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none',
|
||||
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
|
||||
shouldFocus: shouldFocus,
|
||||
shouldAnimate: true,
|
||||
updateSearchScope: false,
|
||||
loop: editor.getOption(EditorOption.find).loop
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return controller.start({
|
||||
forceRevealReplace: true,
|
||||
seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none',
|
||||
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
|
||||
shouldFocus: shouldFocus,
|
||||
shouldAnimate: true,
|
||||
updateSearchScope: false,
|
||||
loop: editor.getOption(EditorOption.find).loop
|
||||
});
|
||||
});
|
||||
|
||||
registerEditorContribution(CommonFindController.ID, FindController);
|
||||
|
||||
export const EditorStartFindAction = new StartFindAction();
|
||||
registerMultiEditorAction(EditorStartFindAction);
|
||||
registerEditorAction(StartFindWithSelectionAction);
|
||||
registerEditorAction(NextMatchFindAction);
|
||||
registerEditorAction(NextMatchFindAction2);
|
||||
@@ -816,8 +813,6 @@ registerEditorAction(PreviousMatchFindAction);
|
||||
registerEditorAction(PreviousMatchFindAction2);
|
||||
registerEditorAction(NextSelectionMatchFindAction);
|
||||
registerEditorAction(PreviousSelectionMatchFindAction);
|
||||
export const EditorStartFindReplaceAction = new StartFindReplaceAction();
|
||||
registerMultiEditorAction(EditorStartFindReplaceAction);
|
||||
|
||||
const FindCommand = EditorCommand.bindToContribution<CommonFindController>(CommonFindController.get);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* Find widget */
|
||||
.monaco-editor .find-widget {
|
||||
position: absolute;
|
||||
z-index: 50;
|
||||
z-index: 35;
|
||||
height: 33px;
|
||||
overflow: hidden;
|
||||
line-height: 19px;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Delayer } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -17,6 +18,7 @@ import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
|
||||
import { withAsyncTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
@@ -55,10 +57,16 @@ function fromSelection(slc: Selection): number[] {
|
||||
return [slc.startLineNumber, slc.startColumn, slc.endLineNumber, slc.endColumn];
|
||||
}
|
||||
|
||||
function executeAction(instantiationService: IInstantiationService, editor: ICodeEditor, action: EditorAction, args?: any): Promise<void> {
|
||||
return instantiationService.invokeFunction((accessor) => {
|
||||
return Promise.resolve(action.runEditorCommand(accessor, editor, args));
|
||||
});
|
||||
}
|
||||
|
||||
suite('FindController', async () => {
|
||||
let queryState: { [key: string]: any; } = {};
|
||||
const queryState: { [key: string]: any; } = {};
|
||||
let clipboardState = '';
|
||||
let serviceCollection = new ServiceCollection();
|
||||
const serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IStorageService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeTarget: Event.None,
|
||||
@@ -164,16 +172,15 @@ suite('FindController', async () => {
|
||||
'ABC',
|
||||
'XYZ',
|
||||
'ABC'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
// The cursor is at the very top, of the file, at the first ABC
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let findState = findController.getState();
|
||||
let startFindAction = new StartFindAction();
|
||||
let nextMatchFindAction = new NextMatchFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findState = findController.getState();
|
||||
const nextMatchFindAction = new NextMatchFindAction();
|
||||
|
||||
// I hit Ctrl+F to show the Find dialog
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
// I type ABC.
|
||||
findState.change({ searchString: 'A' }, true);
|
||||
@@ -221,8 +228,8 @@ suite('FindController', async () => {
|
||||
'import nls = require(\'vs/nls\');'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let nextMatchFindAction = new NextMatchFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const nextMatchFindAction = new NextMatchFindAction();
|
||||
|
||||
editor.setPosition({
|
||||
lineNumber: 1,
|
||||
@@ -244,16 +251,15 @@ suite('FindController', async () => {
|
||||
'var x = (3 * 5)',
|
||||
'var y = (3 * 5)',
|
||||
'var z = (3 * 5)',
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let startFindAction = new StartFindAction();
|
||||
let nextMatchFindAction = new NextMatchFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const nextMatchFindAction = new NextMatchFindAction();
|
||||
|
||||
editor.setSelection(new Selection(1, 9, 1, 13));
|
||||
|
||||
findController.toggleRegex();
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
await nextMatchFindAction.run(null, editor);
|
||||
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]);
|
||||
@@ -268,11 +274,10 @@ suite('FindController', async () => {
|
||||
test('issue #41027: Don\'t replace find input value on replace action if find input is active', async () => {
|
||||
await withAsyncTestCodeEditor([
|
||||
'test',
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
let testRegexString = 'tes.';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let nextMatchFindAction = new NextMatchFindAction();
|
||||
let startFindReplaceAction = new StartFindReplaceAction();
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
const testRegexString = 'tes.';
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const nextMatchFindAction = new NextMatchFindAction();
|
||||
|
||||
findController.toggleRegex();
|
||||
findController.setSearchString(testRegexString);
|
||||
@@ -286,7 +291,7 @@ suite('FindController', async () => {
|
||||
loop: true
|
||||
});
|
||||
await nextMatchFindAction.run(null, editor);
|
||||
await startFindReplaceAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindReplaceAction);
|
||||
|
||||
assert.strictEqual(findController.getState().searchString, testRegexString);
|
||||
|
||||
@@ -301,7 +306,7 @@ suite('FindController', async () => {
|
||||
'var z = (3 * 5)',
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
await findController.start({
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: 'none',
|
||||
@@ -328,12 +333,11 @@ suite('FindController', async () => {
|
||||
test('issue #18111: Regex replace with single space replaces with no space', async () => {
|
||||
await withAsyncTestCodeEditor([
|
||||
'HRESULT OnAmbientPropertyChange(DISPID dispid);'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
let startFindAction = new StartFindAction();
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false);
|
||||
findController.moveToNextMatch();
|
||||
@@ -355,12 +359,11 @@ suite('FindController', async () => {
|
||||
'',
|
||||
'line2',
|
||||
'line3'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
let startFindAction = new StartFindAction();
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false);
|
||||
findController.moveToNextMatch();
|
||||
@@ -384,8 +387,8 @@ suite('FindController', async () => {
|
||||
'([funny]'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
|
||||
|
||||
// toggle regex
|
||||
findController.getState().change({ isRegex: true }, false);
|
||||
@@ -409,14 +412,13 @@ suite('FindController', async () => {
|
||||
'([funny]',
|
||||
'',
|
||||
'([funny]'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let startFindAction = new StartFindAction();
|
||||
let nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const nextSelectionMatchFindAction = new NextSelectionMatchFindAction();
|
||||
|
||||
// cmd+f - open find widget
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
// toggle regex
|
||||
findController.getState().change({ isRegex: true }, false);
|
||||
@@ -442,21 +444,20 @@ suite('FindController', async () => {
|
||||
'XYZ',
|
||||
'ABC',
|
||||
'ABC'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
let startFindAction = new StartFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
// change selection
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
|
||||
// cmd+f - open find widget
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 2, 4));
|
||||
let startFindWithSelectionAction = new StartFindWithSelectionAction();
|
||||
const startFindWithSelectionAction = new StartFindWithSelectionAction();
|
||||
await startFindWithSelectionAction.run(null, editor);
|
||||
let findState = findController.getState();
|
||||
const findState = findController.getState();
|
||||
|
||||
assert.deepStrictEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']);
|
||||
|
||||
@@ -476,13 +477,13 @@ suite('FindController', async () => {
|
||||
'ABC'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
editor.setSelection(new Selection(1, 2, 1, 2));
|
||||
|
||||
let startFindWithSelectionAction = new StartFindWithSelectionAction();
|
||||
const startFindWithSelectionAction = new StartFindWithSelectionAction();
|
||||
startFindWithSelectionAction.run(null, editor);
|
||||
|
||||
let findState = findController.getState();
|
||||
const findState = findController.getState();
|
||||
assert.deepStrictEqual(findState.searchString, 'ABC');
|
||||
findController.dispose();
|
||||
});
|
||||
@@ -494,7 +495,7 @@ suite('FindController query options persistence', async () => {
|
||||
queryState['editor.isRegex'] = false;
|
||||
queryState['editor.matchCase'] = false;
|
||||
queryState['editor.wholeWord'] = false;
|
||||
let serviceCollection = new ServiceCollection();
|
||||
const serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IStorageService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeTarget: Event.None,
|
||||
@@ -518,15 +519,14 @@ suite('FindController query options persistence', async () => {
|
||||
'ABC',
|
||||
'XYZ',
|
||||
'ABC'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
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.ID, TestFindController);
|
||||
let findState = findController.getState();
|
||||
let startFindAction = new StartFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findState = findController.getState();
|
||||
|
||||
// I hit Ctrl+F to show the Find dialog
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
// I type ABC.
|
||||
findState.change({ searchString: 'ABC' }, true);
|
||||
@@ -545,15 +545,14 @@ suite('FindController query options persistence', async () => {
|
||||
'AB',
|
||||
'XYZ',
|
||||
'ABC'
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => {
|
||||
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.ID, TestFindController);
|
||||
let findState = findController.getState();
|
||||
let startFindAction = new StartFindAction();
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findState = findController.getState();
|
||||
|
||||
// I hit Ctrl+F to show the Find dialog
|
||||
await startFindAction.run(null, editor);
|
||||
await executeAction(instantiationService, editor, StartFindAction);
|
||||
|
||||
// I type AB.
|
||||
findState.change({ searchString: 'AB' }, true);
|
||||
@@ -573,7 +572,7 @@ suite('FindController query options persistence', async () => {
|
||||
], { serviceCollection: serviceCollection }, async (editor) => {
|
||||
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.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
findController.toggleRegex();
|
||||
assert.strictEqual(queryState['editor.isRegex'], true);
|
||||
|
||||
@@ -588,7 +587,7 @@ suite('FindController query options persistence', async () => {
|
||||
'var z = (3 * 5)',
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
|
||||
// clipboardState = '';
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findConfig: IFindStartOptions = {
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: 'none',
|
||||
@@ -619,7 +618,7 @@ suite('FindController query options persistence', async () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 2, 1, 2));
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
await findController.start({
|
||||
forceRevealReplace: false,
|
||||
@@ -643,7 +642,7 @@ suite('FindController query options persistence', async () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 2, 1, 3));
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
await findController.start({
|
||||
forceRevealReplace: false,
|
||||
@@ -668,7 +667,7 @@ suite('FindController query options persistence', async () => {
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, async (editor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 6, 2, 1));
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
await findController.start({
|
||||
forceRevealReplace: false,
|
||||
|
||||
@@ -200,13 +200,16 @@ suite('Replace Pattern test', () => {
|
||||
assertReplace(['abc', 'Abc'], 'Def', 'def');
|
||||
assertReplace(['Abc', 'abc'], 'Def', 'Def');
|
||||
assertReplace(['ABC', 'abc'], 'Def', 'DEF');
|
||||
assertReplace(['aBc', 'abc'], 'Def', 'def');
|
||||
assertReplace(['AbC'], 'Def', 'Def');
|
||||
assertReplace(['aBC'], 'Def', 'Def');
|
||||
assertReplace(['aBC'], 'Def', 'def');
|
||||
assertReplace(['aBc'], 'DeF', 'deF');
|
||||
assertReplace(['Foo-Bar'], 'newfoo-newbar', 'Newfoo-Newbar');
|
||||
assertReplace(['Foo-Bar-Abc'], 'newfoo-newbar-newabc', 'Newfoo-Newbar-Newabc');
|
||||
assertReplace(['Foo-Bar-abc'], 'newfoo-newbar', 'Newfoo-newbar');
|
||||
assertReplace(['foo-Bar'], 'newfoo-newbar', 'newfoo-Newbar');
|
||||
assertReplace(['foo-BAR'], 'newfoo-newbar', 'newfoo-NEWBAR');
|
||||
assertReplace(['foO-BAR'], 'NewFoo-NewBar', 'newFoo-NEWBAR');
|
||||
assertReplace(['Foo_Bar'], 'newfoo_newbar', 'Newfoo_Newbar');
|
||||
assertReplace(['Foo_Bar_Abc'], 'newfoo_newbar_newabc', 'Newfoo_Newbar_Newabc');
|
||||
assertReplace(['Foo_Bar_abc'], 'newfoo_newbar', 'Newfoo_newbar');
|
||||
@@ -228,13 +231,16 @@ suite('Replace Pattern test', () => {
|
||||
assertReplace(['abc', 'Abc'], 'Def', 'def');
|
||||
assertReplace(['Abc', 'abc'], 'Def', 'Def');
|
||||
assertReplace(['ABC', 'abc'], 'Def', 'DEF');
|
||||
assertReplace(['aBc', 'abc'], 'Def', 'def');
|
||||
assertReplace(['AbC'], 'Def', 'Def');
|
||||
assertReplace(['aBC'], 'Def', 'Def');
|
||||
assertReplace(['aBC'], 'Def', 'def');
|
||||
assertReplace(['aBc'], 'DeF', 'deF');
|
||||
assertReplace(['Foo-Bar'], 'newfoo-newbar', 'Newfoo-Newbar');
|
||||
assertReplace(['Foo-Bar-Abc'], 'newfoo-newbar-newabc', 'Newfoo-Newbar-Newabc');
|
||||
assertReplace(['Foo-Bar-abc'], 'newfoo-newbar', 'Newfoo-newbar');
|
||||
assertReplace(['foo-Bar'], 'newfoo-newbar', 'newfoo-Newbar');
|
||||
assertReplace(['foo-BAR'], 'newfoo-newbar', 'newfoo-NEWBAR');
|
||||
assertReplace(['foO-BAR'], 'NewFoo-NewBar', 'newFoo-NEWBAR');
|
||||
assertReplace(['Foo_Bar'], 'newfoo_newbar', 'Newfoo_Newbar');
|
||||
assertReplace(['Foo_Bar_Abc'], 'newfoo_newbar_newabc', 'Newfoo_Newbar_Newabc');
|
||||
assertReplace(['Foo_Bar_abc'], 'newfoo_newbar', 'Newfoo_newbar');
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel';
|
||||
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, setCollapseStateForRest, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel';
|
||||
import { FoldingDecorationProvider, foldingCollapsedIcon, foldingExpandedIcon } from './foldingDecorations';
|
||||
import { FoldingRegions, FoldingRegion } from './foldingRanges';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
@@ -429,18 +429,34 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
if (region && region.startLineNumber === lineNumber) {
|
||||
let isCollapsed = region.isCollapsed;
|
||||
if (iconClicked || isCollapsed) {
|
||||
let surrounding = e.event.altKey;
|
||||
let toToggle = [];
|
||||
let recursive = e.event.middleButton || e.event.shiftKey;
|
||||
if (recursive) {
|
||||
for (const r of foldingModel.getRegionsInside(region)) {
|
||||
if (r.isCollapsed === isCollapsed) {
|
||||
if (surrounding) {
|
||||
let filter = (otherRegion: FoldingRegion) => !otherRegion.containedBy(region!) && !region!.containedBy(otherRegion);
|
||||
let toMaybeToggle = foldingModel.getRegionsInside(null, filter);
|
||||
for (const r of toMaybeToggle) {
|
||||
if (r.isCollapsed) {
|
||||
toToggle.push(r);
|
||||
}
|
||||
}
|
||||
// if any surrounding regions are folded, unfold those. Otherwise, fold all surrounding
|
||||
if (toToggle.length === 0) {
|
||||
toToggle = toMaybeToggle;
|
||||
}
|
||||
}
|
||||
// when recursive, first only collapse all children. If all are already folded or there are no children, also fold parent.
|
||||
if (isCollapsed || !recursive || toToggle.length === 0) {
|
||||
toToggle.push(region);
|
||||
else {
|
||||
let recursive = e.event.middleButton || e.event.shiftKey;
|
||||
if (recursive) {
|
||||
for (const r of foldingModel.getRegionsInside(region)) {
|
||||
if (r.isCollapsed === isCollapsed) {
|
||||
toToggle.push(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
// when recursive, first only collapse all children. If all are already folded or there are no children, also fold parent.
|
||||
if (isCollapsed || !recursive || toToggle.length === 0) {
|
||||
toToggle.push(region);
|
||||
}
|
||||
}
|
||||
foldingModel.toggleCollapseState(toToggle);
|
||||
this.reveal({ lineNumber, column: 1 });
|
||||
@@ -821,6 +837,51 @@ class UnfoldAllRegionsAction extends FoldingAction<void> {
|
||||
}
|
||||
}
|
||||
|
||||
class FoldAllRegionsExceptAction extends FoldingAction<void> {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.foldAllExcept',
|
||||
label: nls.localize('foldAllExcept.label', "Fold All Regions Except Selected"),
|
||||
alias: 'Fold All Regions Except Selected',
|
||||
precondition: CONTEXT_FOLDING_ENABLED,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_MINUS),
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
|
||||
let selectedLines = this.getSelectedLines(editor);
|
||||
setCollapseStateForRest(foldingModel, true, selectedLines);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UnfoldAllRegionsExceptAction extends FoldingAction<void> {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.unfoldAllExcept',
|
||||
label: nls.localize('unfoldAllExcept.label', "Unfold All Regions Except Selected"),
|
||||
alias: 'Unfold All Regions Except Selected',
|
||||
precondition: CONTEXT_FOLDING_ENABLED,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_EQUAL),
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
|
||||
let selectedLines = this.getSelectedLines(editor);
|
||||
setCollapseStateForRest(foldingModel, false, selectedLines);
|
||||
}
|
||||
}
|
||||
|
||||
class FoldAllAction extends FoldingAction<void> {
|
||||
|
||||
constructor() {
|
||||
@@ -886,6 +947,8 @@ registerEditorAction(UnfoldAllAction);
|
||||
registerEditorAction(FoldAllBlockCommentsAction);
|
||||
registerEditorAction(FoldAllRegionsAction);
|
||||
registerEditorAction(UnfoldAllRegionsAction);
|
||||
registerEditorAction(FoldAllRegionsExceptAction);
|
||||
registerEditorAction(UnfoldAllRegionsExceptAction);
|
||||
registerEditorAction(ToggleFoldAction);
|
||||
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
|
||||
@@ -368,6 +368,21 @@ export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: n
|
||||
foldingModel.toggleCollapseState(toToggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Folds or unfolds all regions, except if they contain or are contained by a region of one of the blocked lines.
|
||||
* @param doCollapse Whether to collapse or expand
|
||||
* @param blockedLineNumbers the location of regions to not collapse or expand
|
||||
*/
|
||||
export function setCollapseStateForRest(foldingModel: FoldingModel, doCollapse: boolean, blockedLineNumbers: number[]): void {
|
||||
let filteredRegions: FoldingRegion[] = [];
|
||||
for (let lineNumber of blockedLineNumbers) {
|
||||
filteredRegions.push(foldingModel.getAllRegionsAtLine(lineNumber, undefined)[0]);
|
||||
}
|
||||
let filter = (region: FoldingRegion) => filteredRegions.every((filteredRegion) => !filteredRegion.containedBy(region) && !region.containedBy(filteredRegion)) && region.isCollapsed !== doCollapse;
|
||||
let toToggle = foldingModel.getRegionsInside(null, filter);
|
||||
foldingModel.toggleCollapseState(toToggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Folds all regions for which the lines start with a given regex
|
||||
* @param foldingModel the folding model
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel';
|
||||
import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp, setCollapseStateForRest } from 'vs/editor/contrib/folding/foldingModel';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
|
||||
@@ -717,6 +717,56 @@ suite('Folding Model', () => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('setCollapseStateForRest', () => {
|
||||
let lines = [
|
||||
/* 1*/ '//#region',
|
||||
/* 2*/ '//#endregion',
|
||||
/* 3*/ 'class A {',
|
||||
/* 4*/ ' void foo() {',
|
||||
/* 5*/ ' if (true) {',
|
||||
/* 6*/ ' return;',
|
||||
/* 7*/ ' }',
|
||||
/* 8*/ '',
|
||||
/* 9*/ ' if (true) {',
|
||||
/* 10*/ ' return;',
|
||||
/* 11*/ ' }',
|
||||
/* 12*/ ' }',
|
||||
/* 13*/ '}'];
|
||||
|
||||
let textModel = createTextModel(lines.join('\n'));
|
||||
try {
|
||||
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
|
||||
|
||||
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
|
||||
foldingModel.update(ranges);
|
||||
|
||||
let r1 = r(1, 2, false);
|
||||
let r2 = r(3, 12, false);
|
||||
let r3 = r(4, 11, false);
|
||||
let r4 = r(5, 6, false);
|
||||
let r5 = r(9, 10, false);
|
||||
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
|
||||
|
||||
setCollapseStateForRest(foldingModel, true, [5]);
|
||||
assertFoldedRanges(foldingModel, [r1, r5], '1');
|
||||
|
||||
setCollapseStateForRest(foldingModel, false, [5]);
|
||||
assertFoldedRanges(foldingModel, [], '2');
|
||||
|
||||
setCollapseStateForRest(foldingModel, true, [1]);
|
||||
assertFoldedRanges(foldingModel, [r2, r3, r4, r5], '3');
|
||||
|
||||
setCollapseStateForRest(foldingModel, true, [3]);
|
||||
assertFoldedRanges(foldingModel, [r1, r2, r3, r4, r5], '3');
|
||||
|
||||
} finally {
|
||||
textModel.dispose();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('folding decoration', () => {
|
||||
let lines = [
|
||||
/* 1*/ 'class A {',
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { extUri } from 'vs/base/common/resources';
|
||||
import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { LocationLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult, ReferenceProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Location, LocationLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult, ReferenceProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
|
||||
|
||||
|
||||
@@ -37,7 +39,8 @@ function getLocationLinks<T>(
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
return sortAndDeduplicate(result);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,6 +83,22 @@ export function getReferencesAtPosition(model: ITextModel, position: Position, c
|
||||
});
|
||||
}
|
||||
|
||||
export function sortAndDeduplicate(locations: Location[]): Location[] {
|
||||
const result: LocationLink[] = [];
|
||||
let last: LocationLink | undefined;
|
||||
for (const link of locations.sort(compareLocations)) {
|
||||
if (last === undefined || compareLocations(last, link) !== 0) {
|
||||
result.push(link);
|
||||
last = link;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function compareLocations(a: Location, b: Location): number {
|
||||
return extUri.compare(a.uri, b.uri) || Range.compareRangesUsingStarts(a.range, b.range);
|
||||
}
|
||||
|
||||
registerModelAndPositionCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None));
|
||||
registerModelAndPositionCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None));
|
||||
registerModelAndPositionCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None));
|
||||
|
||||
@@ -27,7 +27,7 @@ import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export const ctxReferenceSearchVisible = new RawContextKey<boolean>('referenceSearchVisible', false);
|
||||
export const ctxReferenceSearchVisible = new RawContextKey<boolean>('referenceSearchVisible', false, nls.localize('referenceSearchVisible', "Whether reference peek is visible, like 'Peek References' or 'Peek Definition'"));
|
||||
|
||||
export abstract class ReferencesController implements IEditorContribution {
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider),
|
||||
identityProvider: new IdentityProvider(),
|
||||
openOnSingleClick: true,
|
||||
openOnFocus: true,
|
||||
selectionNavigation: true,
|
||||
overrideStyles: {
|
||||
listBackground: peekView.peekViewResultsBackground
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { defaultGenerator } from 'vs/base/common/idGenerator';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { Location, LocationLink } from 'vs/editor/common/modes';
|
||||
import { LocationLink } from 'vs/editor/common/modes';
|
||||
import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
@@ -19,6 +19,8 @@ import { Constants } from 'vs/base/common/uint';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
import { sortAndDeduplicate } from './goToSymbol';
|
||||
|
||||
export class OneReference {
|
||||
|
||||
readonly id: string = defaultGenerator.nextId();
|
||||
@@ -154,9 +156,9 @@ export class ReferencesModel implements IDisposable {
|
||||
this._links = links;
|
||||
this._title = title;
|
||||
|
||||
// grouping and sorting
|
||||
// grouping, sorting, and de-duplicating
|
||||
const [providersFirst] = links;
|
||||
links.sort(ReferencesModel._compareReferences);
|
||||
links = sortAndDeduplicate(links);
|
||||
|
||||
let current: FileReferences | undefined;
|
||||
for (let link of links) {
|
||||
@@ -166,19 +168,15 @@ export class ReferencesModel implements IDisposable {
|
||||
this.groups.push(current);
|
||||
}
|
||||
|
||||
// append, check for equality first!
|
||||
if (current.children.length === 0 || ReferencesModel._compareReferences(link, current.children[current.children.length - 1]) !== 0) {
|
||||
|
||||
const oneRef = new OneReference(
|
||||
providersFirst === link,
|
||||
current,
|
||||
link.uri,
|
||||
link.targetSelectionRange || link.range,
|
||||
ref => this._onDidChangeReferenceRange.fire(ref)
|
||||
);
|
||||
this.references.push(oneRef);
|
||||
current.children.push(oneRef);
|
||||
}
|
||||
const oneRef = new OneReference(
|
||||
providersFirst === link,
|
||||
current,
|
||||
link.uri,
|
||||
link.targetSelectionRange || link.range,
|
||||
ref => this._onDidChangeReferenceRange.fire(ref)
|
||||
);
|
||||
this.references.push(oneRef);
|
||||
current.children.push(oneRef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +285,4 @@ export class ReferencesModel implements IDisposable {
|
||||
}
|
||||
return this.references[0];
|
||||
}
|
||||
|
||||
private static _compareReferences(a: Location, b: Location): number {
|
||||
return extUri.compare(a.uri, b.uri) || Range.compareRangesUsingStarts(a.range, b.range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export const ctxHasSymbols = new RawContextKey('hasSymbols', false);
|
||||
export const ctxHasSymbols = new RawContextKey('hasSymbols', false, localize('hasSymbols', "Whether there are symbol locations that can be navigated via keyboard-only."));
|
||||
|
||||
export const ISymbolNavigationService = createDecorator<ISymbolNavigationService>('ISymbolNavigationService');
|
||||
|
||||
|
||||
@@ -312,13 +312,9 @@ class ShowDefinitionPreviewHoverAction extends EditorAction {
|
||||
const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
||||
const goto = GotoDefinitionAtPositionEditorContribution.get(editor);
|
||||
const promise = goto.startFindDefinitionFromCursor(position);
|
||||
if (promise) {
|
||||
promise.then(() => {
|
||||
controller.showContentHover(range, HoverStartMode.Immediate, true);
|
||||
});
|
||||
} else {
|
||||
promise.then(() => {
|
||||
controller.showContentHover(range, HoverStartMode.Immediate, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export class MarkerHover implements IHoverPart {
|
||||
}
|
||||
|
||||
const markerCodeActionTrigger: CodeActionTrigger = {
|
||||
type: CodeActionTriggerType.Manual,
|
||||
type: CodeActionTriggerType.Invoke,
|
||||
filter: { include: CodeActionKind.QuickFix }
|
||||
};
|
||||
|
||||
|
||||
@@ -1058,7 +1058,7 @@ export class SnakeCaseAction extends AbstractCaseAction {
|
||||
protected _modifyText(text: string, wordSeparators: string): string {
|
||||
return (text
|
||||
.replace(/(\p{Ll})(\p{Lu})/gmu, '$1_$2')
|
||||
.replace(/([^\b_])(\p{Lu})(\p{Ll})/gmu, '$1_$2$3')
|
||||
.replace(/(\p{Lu}|\p{N})(\p{Lu})(\p{Ll})/gmu, '$1_$2$3')
|
||||
.toLocaleLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -549,7 +549,10 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
`function helloWorld() {
|
||||
return someGlobalObject.printHelloWorld("en", "utf-8");
|
||||
}
|
||||
helloWorld();`.replace(/^\s+/gm, '')
|
||||
helloWorld();`.replace(/^\s+/gm, ''),
|
||||
`'JavaScript'`,
|
||||
'parseHTML4String',
|
||||
'_accessor: ServicesAccessor'
|
||||
], {}, (editor) => {
|
||||
let model = editor.getModel()!;
|
||||
let uppercaseAction = new UpperCaseAction();
|
||||
@@ -659,6 +662,21 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
}
|
||||
hello_world();`.replace(/^\s+/gm, ''));
|
||||
assertSelection(editor, new Selection(14, 1, 17, 15));
|
||||
|
||||
editor.setSelection(new Selection(18, 1, 18, 13));
|
||||
executeAction(snakecaseAction, editor);
|
||||
assert.strictEqual(model.getLineContent(18), `'java_script'`);
|
||||
assertSelection(editor, new Selection(18, 1, 18, 14));
|
||||
|
||||
editor.setSelection(new Selection(19, 1, 19, 17));
|
||||
executeAction(snakecaseAction, editor);
|
||||
assert.strictEqual(model.getLineContent(19), 'parse_html4_string');
|
||||
assertSelection(editor, new Selection(19, 1, 19, 19));
|
||||
|
||||
editor.setSelection(new Selection(20, 1, 20, 28));
|
||||
executeAction(snakecaseAction, editor);
|
||||
assert.strictEqual(model.getLineContent(20), '_accessor: services_accessor');
|
||||
assertSelection(editor, new Selection(20, 1, 20, 29));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
@@ -27,6 +28,16 @@ import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/com
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { CursorState } from 'vs/editor/common/controller/cursorCommon';
|
||||
|
||||
function announceCursorChange(previousCursorState: CursorState[], cursorState: CursorState[]): void {
|
||||
const cursorDiff = cursorState.filter(cs => !previousCursorState.find(pcs => pcs.equals(cs)));
|
||||
if (cursorDiff.length >= 1) {
|
||||
const cursorPositions = cursorDiff.map(cs => `line ${cs.viewState.position.lineNumber} column ${cs.viewState.position.column}`).join(', ');
|
||||
const msg = cursorDiff.length === 1 ? nls.localize('cursorAdded', "Cursor added: {0}", cursorPositions) : nls.localize('cursorsAdded', "Cursors added: {0}", cursorPositions);
|
||||
status(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class InsertCursorAbove extends EditorAction {
|
||||
|
||||
@@ -67,12 +78,14 @@ export class InsertCursorAbove extends EditorAction {
|
||||
}
|
||||
|
||||
viewModel.pushStackElement();
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
viewModel.setCursorStates(
|
||||
args.source,
|
||||
CursorChangeReason.Explicit,
|
||||
CursorMoveCommands.addCursorUp(viewModel, viewModel.getCursorStates(), useLogicalLine)
|
||||
CursorMoveCommands.addCursorUp(viewModel, previousCursorState, useLogicalLine)
|
||||
);
|
||||
viewModel.revealTopMostCursor(args.source);
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +128,14 @@ export class InsertCursorBelow extends EditorAction {
|
||||
}
|
||||
|
||||
viewModel.pushStackElement();
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
viewModel.setCursorStates(
|
||||
args.source,
|
||||
CursorChangeReason.Explicit,
|
||||
CursorMoveCommands.addCursorDown(viewModel, viewModel.getCursorStates(), useLogicalLine)
|
||||
CursorMoveCommands.addCursorDown(viewModel, previousCursorState, useLogicalLine)
|
||||
);
|
||||
viewModel.revealBottomMostCursor(args.source);
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,12 +182,15 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction {
|
||||
|
||||
const model = editor.getModel();
|
||||
const selections = editor.getSelections();
|
||||
const viewModel = editor._getViewModel();
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
let newSelections: Selection[] = [];
|
||||
selections.forEach((sel) => this.getCursorsForSelection(sel, model, newSelections));
|
||||
|
||||
if (newSelections.length > 0) {
|
||||
editor.setSelections(newSelections);
|
||||
}
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,9 +218,12 @@ class InsertCursorAtEndOfLineSelected extends EditorAction {
|
||||
newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn));
|
||||
}
|
||||
|
||||
const viewModel = editor._getViewModel();
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
if (newSelections.length > 0) {
|
||||
editor.setSelections(newSelections);
|
||||
}
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,9 +250,12 @@ class InsertCursorAtTopOfLineSelected extends EditorAction {
|
||||
newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn));
|
||||
}
|
||||
|
||||
const viewModel = editor._getViewModel();
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
if (newSelections.length > 0) {
|
||||
editor.setSelections(newSelections);
|
||||
}
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +673,12 @@ export abstract class MultiCursorSelectionControllerAction extends EditorAction
|
||||
if (!findController) {
|
||||
return;
|
||||
}
|
||||
this._run(multiCursorController, findController);
|
||||
const viewModel = editor._getViewModel();
|
||||
if (viewModel) {
|
||||
const previousCursorState = viewModel.getCursorStates();
|
||||
this._run(multiCursorController, findController);
|
||||
announceCursorChange(previousCursorState, viewModel.getCursorStates());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void;
|
||||
|
||||
@@ -74,6 +74,7 @@ export class ParameterHintsModel extends Disposable {
|
||||
|
||||
this.throttledDelayer = new Delayer(delay);
|
||||
|
||||
this._register(this.editor.onDidBlurEditorWidget(() => this.cancel()));
|
||||
this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
|
||||
this._register(this.editor.onDidChangeModel(e => this.onModelChanged()));
|
||||
this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged()));
|
||||
|
||||
@@ -56,7 +56,7 @@ registerSingleton(IPeekViewService, class implements IPeekViewService {
|
||||
});
|
||||
|
||||
export namespace PeekContext {
|
||||
export const inPeekEditor = new RawContextKey<boolean>('inReferenceSearchEditor', true);
|
||||
export const inPeekEditor = new RawContextKey<boolean>('inReferenceSearchEditor', true, nls.localize('inReferenceSearchEditor', "Whether the current code editor is embedded inside peek"));
|
||||
export const notInPeekEditor = inPeekEditor.toNegated();
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
||||
// 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('gotoLineColumnLabel', "Go to line {0} and character {1}.", lineNumber, column);
|
||||
}
|
||||
|
||||
return localize('gotoLineLabel', "Go to line {0}.", lineNumber);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey<boolean>('renameInputVisible', false);
|
||||
export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey<boolean>('renameInputVisible', false, localize('renameInputVisible', "Whether the rename input widget is visible"));
|
||||
|
||||
export interface RenameInputFieldResult {
|
||||
newName: string;
|
||||
|
||||
@@ -19,6 +19,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { SnippetSession } from './snippetSession';
|
||||
import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export interface ISnippetInsertOptions {
|
||||
overwriteBefore: number;
|
||||
@@ -48,9 +49,9 @@ export class SnippetController2 implements IEditorContribution {
|
||||
return editor.getContribution<SnippetController2>(SnippetController2.ID);
|
||||
}
|
||||
|
||||
static readonly InSnippetMode = new RawContextKey('inSnippetMode', false);
|
||||
static readonly HasNextTabstop = new RawContextKey('hasNextTabstop', false);
|
||||
static readonly HasPrevTabstop = new RawContextKey('hasPrevTabstop', false);
|
||||
static readonly InSnippetMode = new RawContextKey('inSnippetMode', false, localize('inSnippetMode', "Whether the editor in current in snippet mode"));
|
||||
static readonly HasNextTabstop = new RawContextKey('hasNextTabstop', false, localize('hasNextTabstop', "Whether there is a next tab stop when in snippet mode"));
|
||||
static readonly HasPrevTabstop = new RawContextKey('hasPrevTabstop', false, localize('hasPrevTabstop', "Whether there is a previous tab stop when in snippet mode"));
|
||||
|
||||
private readonly _inSnippet: IContextKey<boolean>;
|
||||
private readonly _hasNextTabstop: IContextKey<boolean>;
|
||||
|
||||
@@ -174,8 +174,6 @@ export class CompletionModel {
|
||||
wordLow = word.toLowerCase();
|
||||
}
|
||||
|
||||
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
|
||||
|
||||
// remember the word against which this item was
|
||||
// scored
|
||||
item.word = word;
|
||||
@@ -215,19 +213,19 @@ export class CompletionModel {
|
||||
if (!match) {
|
||||
continue; // NO match
|
||||
}
|
||||
if (compareIgnoreCase(item.completion.filterText, textLabel) === 0) {
|
||||
if (compareIgnoreCase(item.completion.filterText, item.textLabel) === 0) {
|
||||
// filterText and label are actually the same -> use good highlights
|
||||
item.score = match;
|
||||
} else {
|
||||
// re-run the scorer on the label in the hope of a result BUT use the rank
|
||||
// of the filterText-match
|
||||
item.score = anyScore(word, wordLow, wordPos, textLabel, item.labelLow, 0);
|
||||
item.score = anyScore(word, wordLow, wordPos, item.textLabel, item.labelLow, 0);
|
||||
item.score[0] = match[0]; // use score from filterText
|
||||
}
|
||||
|
||||
} else {
|
||||
// by default match `word` against the `label`
|
||||
let match = scoreFn(word, wordLow, wordPos, textLabel, item.labelLow, 0, false);
|
||||
let match = scoreFn(word, wordLow, wordPos, item.textLabel, item.labelLow, 0, false);
|
||||
if (!match) {
|
||||
continue; // NO match
|
||||
}
|
||||
@@ -240,7 +238,7 @@ export class CompletionModel {
|
||||
target.push(item as StrictCompletionItem);
|
||||
|
||||
// update stats
|
||||
labelLengths.push(textLabel.length);
|
||||
labelLengths.push(item.textLabel.length);
|
||||
}
|
||||
|
||||
this._filteredItems = target.sort(this._snippetCompareFn);
|
||||
|
||||
@@ -21,16 +21,17 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export const Context = {
|
||||
Visible: new RawContextKey<boolean>('suggestWidgetVisible', false),
|
||||
DetailsVisible: new RawContextKey<boolean>('suggestWidgetDetailsVisible', false),
|
||||
MultipleSuggestions: new RawContextKey<boolean>('suggestWidgetMultipleSuggestions', false),
|
||||
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true),
|
||||
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('acceptSuggestionOnEnter', true),
|
||||
HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false),
|
||||
InsertMode: new RawContextKey<'insert' | 'replace'>('suggestionInsertMode', undefined),
|
||||
CanResolve: new RawContextKey('suggestionCanResolve', false),
|
||||
Visible: new RawContextKey<boolean>('suggestWidgetVisible', false, localize('suggestWidgetVisible', "Whether suggestion are visible")),
|
||||
DetailsVisible: new RawContextKey<boolean>('suggestWidgetDetailsVisible', false, localize('suggestWidgetDetailsVisible', "Whether suggestion details are visible")),
|
||||
MultipleSuggestions: new RawContextKey<boolean>('suggestWidgetMultipleSuggestions', false, localize('suggestWidgetMultipleSuggestions', "Whether there are multiple suggestions to pick from")),
|
||||
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true, localize('suggestionMakesTextEdit', "Whether inserting the current suggestion yields in a change or has everything already been typed")),
|
||||
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('acceptSuggestionOnEnter', true, localize('acceptSuggestionOnEnter', "Whether suggestions are inserted when pressing Enter")),
|
||||
HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false, localize('suggestionHasInsertAndReplaceRange', "Whether the current suggestion has insert and replace behaviour")),
|
||||
InsertMode: new RawContextKey<'insert' | 'replace'>('suggestionInsertMode', undefined, { type: 'string', description: localize('suggestionInsertMode', "Whether the default behaviour is to insert or replace") }),
|
||||
CanResolve: new RawContextKey('suggestionCanResolve', false, localize('suggestionCanResolve', "Whether the current suggestion supports to resolve further details")),
|
||||
};
|
||||
|
||||
export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar');
|
||||
|
||||
@@ -440,10 +440,9 @@ export class SuggestController implements IEditorContribution {
|
||||
};
|
||||
}
|
||||
|
||||
private _alertCompletionItem({ completion: suggestion }: CompletionItem): void {
|
||||
const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
|
||||
if (isNonEmptyArray(suggestion.additionalTextEdits)) {
|
||||
let msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length);
|
||||
private _alertCompletionItem(item: CompletionItem): void {
|
||||
if (isNonEmptyArray(item.completion.additionalTextEdits)) {
|
||||
let msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", item.textLabel, item.completion.additionalTextEdits.length);
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +223,6 @@ export class SuggestWidget implements IDisposable {
|
||||
accessibilityProvider: {
|
||||
getRole: () => 'option',
|
||||
getAriaLabel: (item: CompletionItem) => {
|
||||
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
|
||||
if (item.isResolved && this._isDetailsVisible()) {
|
||||
const { documentation, detail } = item.completion;
|
||||
const docs = strings.format(
|
||||
@@ -231,9 +230,9 @@ export class SuggestWidget implements IDisposable {
|
||||
detail || '',
|
||||
documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : '');
|
||||
|
||||
return nls.localize('ariaCurrenttSuggestionReadDetails', "{0}, docs: {1}", textLabel, docs);
|
||||
return nls.localize('ariaCurrenttSuggestionReadDetails', "{0}, docs: {1}", item.textLabel, docs);
|
||||
} else {
|
||||
return textLabel;
|
||||
return item.textLabel;
|
||||
}
|
||||
},
|
||||
getWidgetAriaLabel: () => nls.localize('suggest', "Suggest"),
|
||||
@@ -670,7 +669,7 @@ export class SuggestWidget implements IDisposable {
|
||||
this._details.hide();
|
||||
this.element.domNode.classList.remove('shows-details');
|
||||
|
||||
} else if (canExpandCompletionItem(this._list.getFocusedElements()[0]) && (this._state === State.Open || this._state === State.Details || this._state === State.Frozen)) {
|
||||
} else if ((canExpandCompletionItem(this._list.getFocusedElements()[0]) || this._explainMode) && (this._state === State.Open || this._state === State.Details || this._state === State.Frozen)) {
|
||||
// show details widget (iff possible)
|
||||
this._ctxSuggestWidgetDetailsVisible.set(true);
|
||||
this._setDetailsVisible(true);
|
||||
@@ -691,9 +690,13 @@ export class SuggestWidget implements IDisposable {
|
||||
}
|
||||
|
||||
toggleExplainMode(): void {
|
||||
if (this._list.getFocusedElements()[0] && this._isDetailsVisible()) {
|
||||
if (this._list.getFocusedElements()[0]) {
|
||||
this._explainMode = !this._explainMode;
|
||||
this.showDetails(false);
|
||||
if (!this._isDetailsVisible()) {
|
||||
this.toggleDetails();
|
||||
} else {
|
||||
this.showDetails(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,10 +128,12 @@ export class SuggestDetailsWidget {
|
||||
|
||||
if (explainMode) {
|
||||
let md = '';
|
||||
md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name}' with '${item.word}'` : ' (no prefix)'}\n`;
|
||||
md += `distance: ${item.distance}, see localityBonus-setting\n`;
|
||||
md += `score: ${item.score[0]}\n`;
|
||||
md += `prefix: ${item.word ?? '(no prefix)'}\n`;
|
||||
md += `word: ${item.completion.filterText ? item.completion.filterText + ' (filterText)' : item.textLabel}\n`;
|
||||
md += `distance: ${item.distance} (localityBonus-setting)\n`;
|
||||
md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`;
|
||||
md += `commit characters: ${item.completion.commitCharacters?.join('')}\n`;
|
||||
md += `commit_chars: ${item.completion.commitCharacters?.join('')}\n`;
|
||||
documentation = new MarkdownString().appendCodeblock('empty', md);
|
||||
detail = `Provider: ${item.provider._debugDisplayName}`;
|
||||
}
|
||||
|
||||
@@ -162,8 +162,6 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
|
||||
|
||||
renderElement(element: CompletionItem, index: number, data: ISuggestionTemplateData): void {
|
||||
const { completion } = element;
|
||||
const textLabel = typeof completion.label === 'string' ? completion.label : completion.label.name;
|
||||
|
||||
data.root.id = getAriaId(index);
|
||||
data.colorspan.style.backgroundColor = '';
|
||||
|
||||
@@ -183,7 +181,7 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
|
||||
// special logic for 'file' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE);
|
||||
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: element.textLabel }), FileKind.FILE);
|
||||
const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FILE);
|
||||
labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
|
||||
|
||||
@@ -192,7 +190,7 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
labelOptions.extraClasses = flatten([
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER),
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: element.textLabel }), FileKind.FOLDER),
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FOLDER)
|
||||
]);
|
||||
} else {
|
||||
@@ -207,7 +205,7 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
|
||||
labelOptions.matches = [];
|
||||
}
|
||||
|
||||
data.iconLabel.setLabel(textLabel, undefined, labelOptions);
|
||||
data.iconLabel.setLabel(element.textLabel, undefined, labelOptions);
|
||||
if (typeof completion.label === 'string') {
|
||||
data.parametersLabel.textContent = '';
|
||||
data.qualifierLabel.textContent = '';
|
||||
@@ -219,7 +217,7 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
|
||||
data.qualifierLabel.textContent = (completion.label.qualifier || '').replace(/\n.*$/m, '');
|
||||
data.detailsLabel.textContent = (completion.label.type || '').replace(/\n.*$/m, '');
|
||||
data.root.classList.remove('string-label');
|
||||
data.root.title = `${textLabel}${completion.label.parameters ?? ''} ${completion.label.qualifier ?? ''} ${completion.label.type ?? ''}`;
|
||||
data.root.title = `${element.textLabel}${completion.label.parameters ?? ''} ${completion.label.qualifier ?? ''} ${completion.label.type ?? ''}`;
|
||||
}
|
||||
|
||||
if (this._editor.getOption(EditorOption.suggest).showInlineDetails) {
|
||||
|
||||
@@ -50,14 +50,14 @@ export abstract class WordDistance {
|
||||
delete wordRanges[wordUntilPos.word];
|
||||
|
||||
return new class extends WordDistance {
|
||||
distance(anchor: IPosition, suggestion: CompletionItem) {
|
||||
distance(anchor: IPosition, item: CompletionItem) {
|
||||
if (!position.equals(editor.getPosition())) {
|
||||
return 0;
|
||||
}
|
||||
if (suggestion.kind === CompletionItemKind.Keyword) {
|
||||
if (item.kind === CompletionItemKind.Keyword) {
|
||||
return 2 << 20;
|
||||
}
|
||||
let word = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
|
||||
let word = typeof item.label === 'string' ? item.label : item.label.name;
|
||||
let wordLines = wordRanges[word];
|
||||
if (isFalsyOrEmpty(wordLines)) {
|
||||
return 2 << 20;
|
||||
@@ -78,5 +78,3 @@ export abstract class WordDistance {
|
||||
|
||||
abstract distance(anchor: IPosition, suggestion: CompletionItem): number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -746,7 +746,10 @@ suite('WordOperations', () => {
|
||||
const mode = new TestMode();
|
||||
const model = createTextModel('a ""', undefined, languageId);
|
||||
|
||||
withTestCodeEditor(null, { model }, (editor, _) => {
|
||||
withTestCodeEditor(null, {
|
||||
model,
|
||||
autoClosingDelete: 'always'
|
||||
}, (editor, _) => {
|
||||
editor.setPosition(new Position(1, 4));
|
||||
deleteWordLeft(editor); assert.strictEqual(model.getLineContent(1), 'a ');
|
||||
});
|
||||
|
||||
@@ -340,6 +340,7 @@ export abstract class DeleteWordCommand extends EditorCommand {
|
||||
const autoClosingBrackets = editor.getOption(EditorOption.autoClosingBrackets);
|
||||
const autoClosingQuotes = editor.getOption(EditorOption.autoClosingQuotes);
|
||||
const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(model.getLanguageIdentifier().id);
|
||||
const viewModel = editor._getViewModel();
|
||||
|
||||
const commands = selections.map((sel) => {
|
||||
const deleteRange = this._delete({
|
||||
@@ -347,9 +348,11 @@ export abstract class DeleteWordCommand extends EditorCommand {
|
||||
model,
|
||||
selection: sel,
|
||||
whitespaceHeuristics: this._whitespaceHeuristics,
|
||||
autoClosingDelete: editor.getOption(EditorOption.autoClosingDelete),
|
||||
autoClosingBrackets,
|
||||
autoClosingQuotes,
|
||||
autoClosingPairs,
|
||||
autoClosedCharacters: viewModel.getCursorAutoClosedCharacters()
|
||||
}, this._wordNavigationType);
|
||||
return new ReplaceCommand(deleteRange, '');
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ export const Token = api.Token;
|
||||
export const editor = api.editor;
|
||||
export const languages = api.languages;
|
||||
|
||||
if (globals.MonacoEnvironment?.globalAPI || globals.define?.amd) {
|
||||
if (globals.MonacoEnvironment?.globalAPI || (typeof define === 'function' && (<any>define).amd)) {
|
||||
self.monaco = api;
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,10 @@ export class SimpleDialogService implements IDialogService {
|
||||
|
||||
export class SimpleNotificationService implements INotificationService {
|
||||
|
||||
readonly onDidAddNotification: Event<INotification> = Event.None;
|
||||
|
||||
readonly onDidRemoveNotification: Event<INotification> = Event.None;
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private static readonly NO_OP: INotificationHandle = new NoOpNotification();
|
||||
|
||||
@@ -23,17 +23,16 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
In certain cases, the default positioning of the aria container (left: -999em) can cause scrollbars to appear.
|
||||
So here we try to avoid that by using a different technique. See https://stackoverflow.com/a/26032207
|
||||
*/
|
||||
/* See https://github.com/microsoft/monaco-editor/issues/2168#issuecomment-780078600 */
|
||||
.monaco-aria-container {
|
||||
position: absolute !important;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
left: inherit !important;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(50%);
|
||||
}
|
||||
|
||||
/* The hc-black theme is already high contrast optimized */
|
||||
|
||||
@@ -142,9 +142,15 @@ export interface IGlobalEditorOptions {
|
||||
* Theme to be used for rendering.
|
||||
* The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
|
||||
* You can create custom themes via `monaco.editor.defineTheme`.
|
||||
* To switch a theme, use `monaco.editor.setTheme`
|
||||
* To switch a theme, use `monaco.editor.setTheme`.
|
||||
* **NOTE**: The theme might be overwritten if the OS is in high contrast mode, unless `autoDetectHighContrast` is set to false.
|
||||
*/
|
||||
theme?: string;
|
||||
/**
|
||||
* If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme.
|
||||
* Defaults to true.
|
||||
*/
|
||||
autoDetectHighContrast?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,9 +175,15 @@ export interface IStandaloneEditorConstructionOptions extends IEditorConstructio
|
||||
* Initial theme to be used for rendering.
|
||||
* The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
|
||||
* You can create custom themes via `monaco.editor.defineTheme`.
|
||||
* To switch a theme, use `monaco.editor.setTheme`
|
||||
* To switch a theme, use `monaco.editor.setTheme`.
|
||||
* **NOTE**: The theme might be overwritten if the OS is in high contrast mode, unless `autoDetectHighContrast` is set to false.
|
||||
*/
|
||||
theme?: string;
|
||||
/**
|
||||
* If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme.
|
||||
* Defaults to true.
|
||||
*/
|
||||
autoDetectHighContrast?: boolean;
|
||||
/**
|
||||
* An URL to open when Ctrl+H (Windows and Linux) or Cmd+H (OSX) is pressed in
|
||||
* the accessibility help dialog in the editor.
|
||||
@@ -189,9 +201,15 @@ export interface IDiffEditorConstructionOptions extends IDiffEditorOptions {
|
||||
* Initial theme to be used for rendering.
|
||||
* The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
|
||||
* You can create custom themes via `monaco.editor.defineTheme`.
|
||||
* To switch a theme, use `monaco.editor.setTheme`
|
||||
* To switch a theme, use `monaco.editor.setTheme`.
|
||||
* **NOTE**: The theme might be overwritten if the OS is in high contrast mode, unless `autoDetectHighContrast` is set to false.
|
||||
*/
|
||||
theme?: string;
|
||||
/**
|
||||
* If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme.
|
||||
* Defaults to true.
|
||||
*/
|
||||
autoDetectHighContrast?: boolean;
|
||||
}
|
||||
|
||||
export interface IStandaloneCodeEditor extends ICodeEditor {
|
||||
@@ -377,6 +395,9 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
|
||||
if (typeof options.theme === 'string') {
|
||||
themeService.setTheme(options.theme);
|
||||
}
|
||||
if (typeof options.autoDetectHighContrast !== 'undefined') {
|
||||
themeService.setAutoDetectHighContrast(Boolean(options.autoDetectHighContrast));
|
||||
}
|
||||
let _model: ITextModel | null | undefined = options.model;
|
||||
delete options.model;
|
||||
super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, keybindingService, themeService, notificationService, accessibilityService);
|
||||
@@ -415,6 +436,9 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
|
||||
if (typeof newOptions.theme === 'string') {
|
||||
this._standaloneThemeService.setTheme(newOptions.theme);
|
||||
}
|
||||
if (typeof newOptions.autoDetectHighContrast !== 'undefined') {
|
||||
this._standaloneThemeService.setAutoDetectHighContrast(Boolean(newOptions.autoDetectHighContrast));
|
||||
}
|
||||
super.updateOptions(newOptions);
|
||||
}
|
||||
|
||||
@@ -461,7 +485,10 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
|
||||
updateConfigurationService(configurationService, options, true);
|
||||
const themeDomRegistration = (<StandaloneThemeServiceImpl>themeService).registerEditorContainer(domElement);
|
||||
if (typeof options.theme === 'string') {
|
||||
options.theme = themeService.setTheme(options.theme);
|
||||
themeService.setTheme(options.theme);
|
||||
}
|
||||
if (typeof options.autoDetectHighContrast !== 'undefined') {
|
||||
themeService.setAutoDetectHighContrast(Boolean(options.autoDetectHighContrast));
|
||||
}
|
||||
|
||||
super(domElement, options, {}, clipboardService, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, editorProgressService);
|
||||
@@ -485,6 +512,9 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
|
||||
if (typeof newOptions.theme === 'string') {
|
||||
this._standaloneThemeService.setTheme(newOptions.theme);
|
||||
}
|
||||
if (typeof newOptions.autoDetectHighContrast !== 'undefined') {
|
||||
this._standaloneThemeService.setAutoDetectHighContrast(Boolean(newOptions.autoDetectHighContrast));
|
||||
}
|
||||
super.updateOptions(newOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,40 @@ import { windowOpenNoOpener } from 'vs/base/browser/dom';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl';
|
||||
import { CodeEditorServiceImpl, GlobalStyleSheet } from 'vs/editor/browser/services/codeEditorServiceImpl';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export class StandaloneCodeEditorServiceImpl extends CodeEditorServiceImpl {
|
||||
|
||||
private readonly _editorIsOpen: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
styleSheet: GlobalStyleSheet | null,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
) {
|
||||
super(styleSheet, themeService);
|
||||
this.onCodeEditorAdd(() => this._checkContextKey());
|
||||
this.onCodeEditorRemove(() => this._checkContextKey());
|
||||
this._editorIsOpen = contextKeyService.createKey('editorIsOpen', false);
|
||||
}
|
||||
|
||||
private _checkContextKey(): void {
|
||||
let hasCodeEditor = false;
|
||||
for (const editor of this.listCodeEditors()) {
|
||||
if (!editor.isSimpleWidget) {
|
||||
hasCodeEditor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._editorIsOpen.set(hasCodeEditor);
|
||||
}
|
||||
|
||||
public getActiveCodeEditor(): ICodeEditor | null {
|
||||
return null; // not supported in the standalone case
|
||||
}
|
||||
|
||||
@@ -160,7 +160,9 @@ export module StaticServices {
|
||||
|
||||
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
|
||||
|
||||
export const codeEditorService = define(ICodeEditorService, (o) => new StandaloneCodeEditorServiceImpl(standaloneThemeService.get(o)));
|
||||
export const contextKeyService = define(IContextKeyService, (o) => new ContextKeyService(configurationService.get(o)));
|
||||
|
||||
export const codeEditorService = define(ICodeEditorService, (o) => new StandaloneCodeEditorServiceImpl(null, contextKeyService.get(o), standaloneThemeService.get(o)));
|
||||
|
||||
export const editorProgressService = define(IEditorProgressService, () => new SimpleEditorProgressService());
|
||||
|
||||
@@ -186,6 +188,7 @@ export class DynamicStandaloneServices extends Disposable {
|
||||
const telemetryService = this.get(ITelemetryService);
|
||||
const themeService = this.get(IThemeService);
|
||||
const logService = this.get(ILogService);
|
||||
const contextKeyService = this.get(IContextKeyService);
|
||||
|
||||
let ensure = <T>(serviceId: ServiceIdentifier<T>, factory: () => T): T => {
|
||||
let value: T | null = null;
|
||||
@@ -199,8 +202,6 @@ export class DynamicStandaloneServices extends Disposable {
|
||||
return value;
|
||||
};
|
||||
|
||||
let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService)));
|
||||
|
||||
ensure(IAccessibilityService, () => new AccessibilityService(contextKeyService, configurationService));
|
||||
|
||||
ensure(IListService, () => new ListService(themeService));
|
||||
|
||||
@@ -198,17 +198,21 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
|
||||
private readonly _environment: IEnvironmentService = Object.create(null);
|
||||
private readonly _knownThemes: Map<string, StandaloneTheme>;
|
||||
private _autoDetectHighContrast: boolean;
|
||||
private _codiconCSS: string;
|
||||
private _themeCSS: string;
|
||||
private _allCSS: string;
|
||||
private _globalStyleElement: HTMLStyleElement | null;
|
||||
private _styleElements: HTMLStyleElement[];
|
||||
private _colorMapOverride: Color[] | null;
|
||||
private _desiredTheme!: IStandaloneTheme;
|
||||
private _theme!: IStandaloneTheme;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._autoDetectHighContrast = true;
|
||||
|
||||
this._knownThemes = new Map<string, StandaloneTheme>();
|
||||
this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME));
|
||||
this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME));
|
||||
@@ -228,6 +232,10 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
this._codiconCSS = iconsStyleSheet.getCSS();
|
||||
this._updateCSS();
|
||||
});
|
||||
|
||||
window.matchMedia('(forced-colors: active)').addEventListener('change', () => {
|
||||
this._updateActualTheme();
|
||||
});
|
||||
}
|
||||
|
||||
public registerEditorContainer(domNode: HTMLElement): IDisposable {
|
||||
@@ -281,7 +289,7 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this._theme && this._theme.themeName === themeName) {
|
||||
if (this._theme.themeName === themeName) {
|
||||
this.setTheme(themeName); // refresh theme
|
||||
}
|
||||
}
|
||||
@@ -295,20 +303,34 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
this._updateThemeOrColorMap();
|
||||
}
|
||||
|
||||
public setTheme(themeName: string): string {
|
||||
public setTheme(themeName: string): void {
|
||||
let theme: StandaloneTheme;
|
||||
if (this._knownThemes.has(themeName)) {
|
||||
theme = this._knownThemes.get(themeName)!;
|
||||
} else {
|
||||
theme = this._knownThemes.get(VS_THEME_NAME)!;
|
||||
}
|
||||
this._desiredTheme = theme;
|
||||
this._updateActualTheme();
|
||||
}
|
||||
|
||||
private _updateActualTheme(): void {
|
||||
const theme = (
|
||||
this._autoDetectHighContrast && window.matchMedia(`(forced-colors: active)`).matches
|
||||
? this._knownThemes.get(HC_BLACK_THEME_NAME)!
|
||||
: this._desiredTheme
|
||||
);
|
||||
if (this._theme === theme) {
|
||||
// Nothing to do
|
||||
return theme.id;
|
||||
return;
|
||||
}
|
||||
this._theme = theme;
|
||||
this._updateThemeOrColorMap();
|
||||
return theme.id;
|
||||
}
|
||||
|
||||
public setAutoDetectHighContrast(autoDetectHighContrast: boolean): void {
|
||||
this._autoDetectHighContrast = autoDetectHighContrast;
|
||||
this._updateActualTheme();
|
||||
}
|
||||
|
||||
private _updateThemeOrColorMap(): void {
|
||||
|
||||
@@ -29,7 +29,9 @@ export interface IStandaloneTheme extends IColorTheme {
|
||||
export interface IStandaloneThemeService extends IThemeService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
setTheme(themeName: string): string;
|
||||
setTheme(themeName: string): void;
|
||||
|
||||
setAutoDetectHighContrast(autoDetectHighContrast: boolean): void;
|
||||
|
||||
defineTheme(themeName: string, themeData: IStandaloneThemeData): void;
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ suite('TokenizationSupport2Adapter', () => {
|
||||
public setTheme(themeName: string): string {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
public setAutoDetectHighContrast(autoDetectHighContrast: boolean): void {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
public defineTheme(themeName: string, themeData: IStandaloneThemeData): void {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
@@ -1047,6 +1047,21 @@ suite('Editor Controller - Cursor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('issue #118062: Column selection cannot select first position of a line', () => {
|
||||
withTestCodeEditor([
|
||||
'hello world',
|
||||
].join('\n'), {}, (editor, viewModel) => {
|
||||
|
||||
moveTo(editor, viewModel, 1, 2, false);
|
||||
assertCursor(viewModel, new Position(1, 2));
|
||||
|
||||
CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});
|
||||
assertCursor(viewModel, [
|
||||
new Selection(1, 2, 1, 1)
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('column select with keyboard', () => {
|
||||
withTestCodeEditor([
|
||||
'var gulp = require("gulp");',
|
||||
@@ -5311,6 +5326,41 @@ suite('autoClosingPairs', () => {
|
||||
mode.dispose();
|
||||
});
|
||||
|
||||
test('issue #118270 - auto closing deletes only those characters that it inserted', () => {
|
||||
let mode = new AutoClosingMode();
|
||||
usingCursor({
|
||||
text: [
|
||||
'',
|
||||
'y=();'
|
||||
],
|
||||
languageIdentifier: mode.getLanguageIdentifier()
|
||||
}, (editor, model, viewModel) => {
|
||||
assertCursor(viewModel, new Position(1, 1));
|
||||
|
||||
viewModel.type('x=(', 'keyboard');
|
||||
assert.strictEqual(model.getLineContent(1), 'x=()');
|
||||
|
||||
viewModel.type('asd', 'keyboard');
|
||||
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
|
||||
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
assert.strictEqual(model.getLineContent(1), 'x=()');
|
||||
|
||||
// delete closing char!
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
assert.strictEqual(model.getLineContent(1), 'x=');
|
||||
|
||||
// do not delete closing char!
|
||||
viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
assert.strictEqual(model.getLineContent(2), 'y=);');
|
||||
|
||||
});
|
||||
mode.dispose();
|
||||
});
|
||||
|
||||
test('issue #78527 - does not close quote on odd count', () => {
|
||||
let mode = new AutoClosingMode();
|
||||
usingCursor({
|
||||
|
||||
@@ -58,12 +58,12 @@ suite('Decoration Render Options', () => {
|
||||
borderColor: 'yellow'
|
||||
};
|
||||
test('register and resolve decoration type', () => {
|
||||
let s = new TestCodeEditorServiceImpl(themeServiceMock);
|
||||
let s = new TestCodeEditorServiceImpl(null, themeServiceMock);
|
||||
s.registerDecorationType('example', options);
|
||||
assert.notStrictEqual(s.resolveDecorationOptions('example', false), undefined);
|
||||
});
|
||||
test('remove decoration type', () => {
|
||||
let s = new TestCodeEditorServiceImpl(themeServiceMock);
|
||||
let s = new TestCodeEditorServiceImpl(null, themeServiceMock);
|
||||
s.registerDecorationType('example', options);
|
||||
assert.notStrictEqual(s.resolveDecorationOptions('example', false), undefined);
|
||||
s.removeDecorationType('example');
|
||||
@@ -76,7 +76,7 @@ suite('Decoration Render Options', () => {
|
||||
|
||||
test('css properties', () => {
|
||||
const styleSheet = new TestGlobalStyleSheet();
|
||||
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
|
||||
const s = new TestCodeEditorServiceImpl(styleSheet, themeServiceMock);
|
||||
s.registerDecorationType('example', options);
|
||||
const sheet = readStyleSheet(styleSheet);
|
||||
assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
|
||||
@@ -93,7 +93,7 @@ suite('Decoration Render Options', () => {
|
||||
const themeService = new TestThemeService(new TestColorTheme({
|
||||
editorBackground: '#FF0000'
|
||||
}));
|
||||
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
|
||||
const s = new TestCodeEditorServiceImpl(styleSheet, themeService);
|
||||
s.registerDecorationType('example', options);
|
||||
assert.strictEqual(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ff0000;border-color:transparent;box-sizing: border-box;}');
|
||||
|
||||
@@ -126,7 +126,7 @@ suite('Decoration Render Options', () => {
|
||||
editorBackground: '#FF0000',
|
||||
infoForeground: '#444444'
|
||||
}));
|
||||
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
|
||||
const s = new TestCodeEditorServiceImpl(styleSheet, themeService);
|
||||
s.registerDecorationType('example', options);
|
||||
const expected = [
|
||||
'.vs-dark.monaco-editor .ced-example-4::after, .hc-black.monaco-editor .ced-example-4::after {color:#444444 !important;}',
|
||||
@@ -142,7 +142,7 @@ suite('Decoration Render Options', () => {
|
||||
|
||||
test('css properties, gutterIconPaths', () => {
|
||||
const styleSheet = new TestGlobalStyleSheet();
|
||||
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
|
||||
const s = new TestCodeEditorServiceImpl(styleSheet, themeServiceMock);
|
||||
|
||||
// URI, only minimal encoding
|
||||
s.registerDecorationType('example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
|
||||
|
||||
@@ -127,6 +127,20 @@ suite('OpenerService', function () {
|
||||
assert.equal(openCount, 2);
|
||||
});
|
||||
|
||||
test('links aren\'t manipulated before being passed to validator: PR #118226', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
openerService.registerValidator({
|
||||
shouldOpen: (resource) => {
|
||||
// We don't want it to convert strings into URIs
|
||||
assert.strictEqual(resource instanceof URI, false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
});
|
||||
await openerService.open('https://wwww.microsoft.com');
|
||||
await openerService.open('https://www.microsoft.com??params=CountryCode%3DUSA%26Name%3Dvscode"');
|
||||
});
|
||||
|
||||
test('links validated by multiple validators', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
|
||||
@@ -41,6 +43,13 @@ export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor {
|
||||
// Never create a view
|
||||
return [null! as View, false];
|
||||
}
|
||||
private _hasTextFocus = false;
|
||||
public setHasTextFocus(hasTextFocus: boolean): void {
|
||||
this._hasTextFocus = hasTextFocus;
|
||||
}
|
||||
public hasTextFocus(): boolean {
|
||||
return this._hasTextFocus;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Testing utils
|
||||
@@ -79,6 +88,11 @@ export interface TestCodeEditorCreationOptions extends editorOptions.IEditorOpti
|
||||
*/
|
||||
model?: ITextModel;
|
||||
serviceCollection?: ServiceCollection;
|
||||
/**
|
||||
* If the editor has text focus.
|
||||
* Defaults to true.
|
||||
*/
|
||||
hasTextFocus?: boolean;
|
||||
}
|
||||
|
||||
export function withTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {
|
||||
@@ -99,7 +113,7 @@ export function withTestCodeEditor(text: string | string[] | null, options: Test
|
||||
editor.dispose();
|
||||
}
|
||||
|
||||
export async function withAsyncTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => Promise<void>): Promise<void> {
|
||||
export async function withAsyncTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => Promise<void>): Promise<void> {
|
||||
// create a model if necessary and remember it in order to dispose it.
|
||||
if (!options.model) {
|
||||
if (typeof text === 'string') {
|
||||
@@ -109,15 +123,20 @@ export async function withAsyncTestCodeEditor(text: string | string[] | null, op
|
||||
}
|
||||
}
|
||||
|
||||
const editor = createTestCodeEditor(options);
|
||||
const [instantiationService, editor] = doCreateTestCodeEditor(options);
|
||||
const viewModel = editor.getViewModel()!;
|
||||
viewModel.setHasFocus(true);
|
||||
await callback(<ITestCodeEditor>editor, editor.getViewModel()!);
|
||||
await callback(<ITestCodeEditor>editor, editor.getViewModel()!, instantiationService);
|
||||
|
||||
editor.dispose();
|
||||
}
|
||||
|
||||
export function createTestCodeEditor(options: TestCodeEditorCreationOptions): ITestCodeEditor {
|
||||
const [, editor] = doCreateTestCodeEditor(options);
|
||||
return editor;
|
||||
}
|
||||
|
||||
function doCreateTestCodeEditor(options: TestCodeEditorCreationOptions): [IInstantiationService, ITestCodeEditor] {
|
||||
|
||||
const model = options.model;
|
||||
delete options.model;
|
||||
@@ -142,6 +161,9 @@ export function createTestCodeEditor(options: TestCodeEditorCreationOptions): IT
|
||||
if (!services.has(IThemeService)) {
|
||||
services.set(IThemeService, new TestThemeService());
|
||||
}
|
||||
if (!services.has(ITelemetryService)) {
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
|
||||
contributions: []
|
||||
@@ -152,6 +174,10 @@ export function createTestCodeEditor(options: TestCodeEditorCreationOptions): IT
|
||||
options,
|
||||
codeEditorWidgetOptions
|
||||
);
|
||||
if (typeof options.hasTextFocus === 'undefined') {
|
||||
options.hasTextFocus = true;
|
||||
}
|
||||
editor.setHasTextFocus(options.hasTextFocus);
|
||||
editor.setModel(model);
|
||||
return <ITestCodeEditor>editor;
|
||||
return [instantiationService, <ITestCodeEditor>editor];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { writeUInt16LE } from 'vs/base/common/buffer';
|
||||
import { decodeUTF16LE } from 'vs/editor/common/core/stringBuilder';
|
||||
|
||||
suite('decodeUTF16LE', () => {
|
||||
|
||||
test('issue #118041: unicode character undo bug 1', () => {
|
||||
const buff = new Uint8Array(2);
|
||||
writeUInt16LE(buff, ''.charCodeAt(0), 0);
|
||||
const actual = decodeUTF16LE(buff, 0, 1);
|
||||
assert.deepStrictEqual(actual, '');
|
||||
});
|
||||
|
||||
test('issue #118041: unicode character undo bug 2', () => {
|
||||
const buff = new Uint8Array(4);
|
||||
writeUInt16LE(buff, 'a'.charCodeAt(0), 0);
|
||||
writeUInt16LE(buff, 'a'.charCodeAt(1), 2);
|
||||
const actual = decodeUTF16LE(buff, 0, 2);
|
||||
assert.deepStrictEqual(actual, 'a');
|
||||
});
|
||||
|
||||
test('issue #118041: unicode character undo bug 3', () => {
|
||||
const buff = new Uint8Array(6);
|
||||
writeUInt16LE(buff, 'ab'.charCodeAt(0), 0);
|
||||
writeUInt16LE(buff, 'ab'.charCodeAt(1), 2);
|
||||
writeUInt16LE(buff, 'ab'.charCodeAt(2), 4);
|
||||
const actual = decodeUTF16LE(buff, 0, 3);
|
||||
assert.deepStrictEqual(actual, 'ab');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -886,4 +886,34 @@ suite('Editor Diff - DiffComputer', () => {
|
||||
];
|
||||
assertDiff(original, modified, expected, false, false, false);
|
||||
});
|
||||
|
||||
test('issue #119051: gives preference to fewer diff hunks', () => {
|
||||
const original = [
|
||||
'1',
|
||||
'',
|
||||
'',
|
||||
'2',
|
||||
'',
|
||||
];
|
||||
const modified = [
|
||||
'1',
|
||||
'',
|
||||
'1.5',
|
||||
'',
|
||||
'',
|
||||
'2',
|
||||
'',
|
||||
'3',
|
||||
'',
|
||||
];
|
||||
const expected = [
|
||||
createLineChange(
|
||||
2, 0, 3, 4
|
||||
),
|
||||
createLineChange(
|
||||
5, 0, 8, 9
|
||||
)
|
||||
];
|
||||
assertDiff(original, modified, expected, false, false, false);
|
||||
});
|
||||
});
|
||||
|
||||
31
lib/vscode/src/vs/editor/test/common/model/editStack.test.ts
Normal file
31
lib/vscode/src/vs/editor/test/common/model/editStack.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { EndOfLineSequence } from 'vs/editor/common/model';
|
||||
import { SingleModelEditStackData } from 'vs/editor/common/model/editStack';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { TextChange } from 'vs/editor/common/model/textChange';
|
||||
|
||||
suite('EditStack', () => {
|
||||
|
||||
test('issue #118041: unicode character undo bug', () => {
|
||||
const stackData = new SingleModelEditStackData(
|
||||
1,
|
||||
2,
|
||||
EndOfLineSequence.LF,
|
||||
EndOfLineSequence.LF,
|
||||
[new Selection(10, 2, 10, 2)],
|
||||
[new Selection(10, 1, 10, 1)],
|
||||
[new TextChange(428, '', 428, '')]
|
||||
);
|
||||
|
||||
const buff = stackData.serialize();
|
||||
const actual = SingleModelEditStackData.deserialize(buff);
|
||||
|
||||
assert.deepStrictEqual(actual, stackData);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -119,6 +119,19 @@ suite('TextChangeCompressor', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// test('issue #118041', () => {
|
||||
// assertCompression(
|
||||
// '',
|
||||
// [
|
||||
// { offset: 0, length: 1, text: '' },
|
||||
// ],
|
||||
// [
|
||||
// { offset: 1, length: 0, text: 'Z' },
|
||||
// { offset: 3, length: 3, text: 'Y' },
|
||||
// ]
|
||||
// );
|
||||
// })
|
||||
|
||||
test('gen1', () => {
|
||||
assertCompression(
|
||||
'kxm',
|
||||
@@ -267,3 +280,16 @@ suite('TextChangeCompressor', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite('TextChange', () => {
|
||||
|
||||
test('issue #118041: unicode character undo bug', () => {
|
||||
const textChange = new TextChange(428, '', 428, '');
|
||||
const buff = new Uint8Array(textChange.writeSize());
|
||||
textChange.write(buff, 0);
|
||||
const actual: TextChange[] = [];
|
||||
TextChange.read(buff, 0, actual);
|
||||
assert.deepStrictEqual(actual[0], textChange);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1062,4 +1062,11 @@ suite('TextModel.createSnapshot', () => {
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('issue #119632: invalid range', () => {
|
||||
const model = createTextModel('hello world!');
|
||||
const actual = model._validateRangeRelaxedNoAllocations(new Range(<any>undefined, 0, <any>undefined, 1));
|
||||
assert.deepStrictEqual(actual, new Range(1, 1, 1, 1));
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1643,8 +1643,8 @@ suite('viewLineRenderer.renderLine 2', () => {
|
||||
0,
|
||||
createViewLineTokens([createPart(0, 3)]),
|
||||
[
|
||||
new LineDecoration(1, 2, 'before', InlineDecorationType.Before),
|
||||
new LineDecoration(0, 1, 'after', InlineDecorationType.After),
|
||||
new LineDecoration(1, 1, 'before', InlineDecorationType.Before),
|
||||
new LineDecoration(1, 1, 'after', InlineDecorationType.After),
|
||||
],
|
||||
2,
|
||||
0,
|
||||
@@ -1668,6 +1668,47 @@ suite('viewLineRenderer.renderLine 2', () => {
|
||||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
test('issue #118759: enable multiple text editor decorations in empty lines', () => {
|
||||
|
||||
let actual = renderViewLine(new RenderLineInput(
|
||||
true,
|
||||
true,
|
||||
'',
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
createViewLineTokens([createPart(0, 3)]),
|
||||
[
|
||||
new LineDecoration(1, 1, 'after1', InlineDecorationType.After),
|
||||
new LineDecoration(1, 1, 'after2', InlineDecorationType.After),
|
||||
new LineDecoration(1, 1, 'before1', InlineDecorationType.Before),
|
||||
new LineDecoration(1, 1, 'before2', InlineDecorationType.Before),
|
||||
],
|
||||
2,
|
||||
0,
|
||||
10,
|
||||
10,
|
||||
10,
|
||||
10000,
|
||||
'none',
|
||||
false,
|
||||
false,
|
||||
null
|
||||
));
|
||||
|
||||
let expected = [
|
||||
'<span>',
|
||||
'<span class="before1"></span>',
|
||||
'<span class="before2"></span>',
|
||||
'<span class="after1"></span>',
|
||||
'<span class="after2"></span>',
|
||||
'</span>'
|
||||
].join('');
|
||||
|
||||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
test('issue #38935: GitLens end-of-line blame no longer rendering', () => {
|
||||
|
||||
let actual = renderViewLine(new RenderLineInput(
|
||||
@@ -2065,6 +2106,38 @@ suite('viewLineRenderer.renderLine 2', () => {
|
||||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
test('issue #119416: Delete Control Character (U+007F / ) displayed as space', () => {
|
||||
const actual = renderViewLine(new RenderLineInput(
|
||||
false,
|
||||
false,
|
||||
'[' + String.fromCharCode(127) + '] [' + String.fromCharCode(0) + ']',
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
createViewLineTokens([createPart(7, 3)]),
|
||||
[],
|
||||
4,
|
||||
0,
|
||||
10,
|
||||
10,
|
||||
10,
|
||||
10000,
|
||||
'none',
|
||||
true,
|
||||
true,
|
||||
null
|
||||
));
|
||||
|
||||
const expected = [
|
||||
'<span>',
|
||||
'<span class="mtk3">[\u2421]\u00a0[\u2400]</span>',
|
||||
'</span>'
|
||||
].join('');
|
||||
|
||||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
|
||||
function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {
|
||||
let renderLineOutput = renderViewLine(new RenderLineInput(
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import * as fs from 'fs';
|
||||
// import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
// import { getPathFromAmdModule } from 'vs/base/test/node/testUtils';
|
||||
// import { parse } from 'vs/editor/common/modes/tokenization/typescript';
|
||||
import { toStandardTokenType } from 'vs/editor/common/modes/supports/tokenization';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user