mirror of
https://github.com/coder/code-server.git
synced 2026-05-08 21:37:27 +02:00
Update to VS Code 1.52.1
This commit is contained in:
@@ -502,6 +502,16 @@ const editorConfiguration: IConfigurationNode = {
|
||||
default: true,
|
||||
description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.")
|
||||
},
|
||||
'editor.wordBasedSuggestionsMode': {
|
||||
enum: ['currentDocument', 'matchingDocuments', 'allDocuments'],
|
||||
default: 'matchingDocuments',
|
||||
enumDescriptions: [
|
||||
nls.localize('wordBasedSuggestionsMode.currentDocument', 'Only suggest words from the active document.'),
|
||||
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 form what documents word based completions are computed.")
|
||||
},
|
||||
'editor.semanticHighlighting.enabled': {
|
||||
enum: [true, false, 'configuredByTheme'],
|
||||
enumDescriptions: [
|
||||
@@ -546,6 +556,16 @@ const editorConfiguration: IConfigurationNode = {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.")
|
||||
},
|
||||
'diffEditor.wordWrap': {
|
||||
type: 'string',
|
||||
enum: ['off', 'on', 'inherit'],
|
||||
default: 'inherit',
|
||||
markdownEnumDescriptions: [
|
||||
nls.localize('wordWrap.off', "Lines will never wrap."),
|
||||
nls.localize('wordWrap.on', "Lines will wrap at the viewport width."),
|
||||
nls.localize('wordWrap.inherit', "Lines will wrap according to the `#editor.wordWrap#` setting."),
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,9 +144,13 @@ export interface IEditorOptions {
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/**
|
||||
* Rename matching regions on type.
|
||||
* Enable linked editing.
|
||||
* Defaults to false.
|
||||
*/
|
||||
linkedEditing?: boolean;
|
||||
/**
|
||||
* deprecated, use linkedEditing instead
|
||||
*/
|
||||
renameOnType?: boolean;
|
||||
/**
|
||||
* Should the editor render validation decorations.
|
||||
@@ -260,6 +264,14 @@ export interface IEditorOptions {
|
||||
* Defaults to "off".
|
||||
*/
|
||||
wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded';
|
||||
/**
|
||||
* Override the `wordWrap` setting.
|
||||
*/
|
||||
wordWrapOverride1?: 'off' | 'on' | 'inherit';
|
||||
/**
|
||||
* Override the `wordWrapOverride1` setting.
|
||||
*/
|
||||
wordWrapOverride2?: 'off' | 'on' | 'inherit';
|
||||
/**
|
||||
* Control the wrapping of the editor.
|
||||
* When `wordWrap` = "off", the lines will never wrap.
|
||||
@@ -269,11 +281,6 @@ export interface IEditorOptions {
|
||||
* Defaults to 80.
|
||||
*/
|
||||
wordWrapColumn?: number;
|
||||
/**
|
||||
* Force word wrapping when the text appears to be of a minified/generated file.
|
||||
* Defaults to true.
|
||||
*/
|
||||
wordWrapMinified?: boolean;
|
||||
/**
|
||||
* Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'.
|
||||
* Defaults to 'same' in vscode and to 'none' in monaco-editor.
|
||||
@@ -370,6 +377,10 @@ export interface IEditorOptions {
|
||||
* Suggest options.
|
||||
*/
|
||||
suggest?: ISuggestOptions;
|
||||
/**
|
||||
* Smart select opptions;
|
||||
*/
|
||||
smartSelect?: ISmartSelectOptions;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -416,6 +427,11 @@ export interface IEditorOptions {
|
||||
* Defaults to advanced.
|
||||
*/
|
||||
autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full';
|
||||
/**
|
||||
* Emulate selection behaviour of tab characters when using spaces for indentation.
|
||||
* This means selection will stick to tab stops.
|
||||
*/
|
||||
stickyTabStops?: boolean;
|
||||
/**
|
||||
* Enable format on type.
|
||||
* Defaults to false.
|
||||
@@ -491,6 +507,14 @@ export interface IEditorOptions {
|
||||
* Defaults to true.
|
||||
*/
|
||||
codeLens?: boolean;
|
||||
/**
|
||||
* Code lens font family. Defaults to editor font family.
|
||||
*/
|
||||
codeLensFontFamily?: string;
|
||||
/**
|
||||
* Code lens font size. Default to 90% of the editor font size
|
||||
*/
|
||||
codeLensFontSize?: number;
|
||||
/**
|
||||
* Control the behavior and rendering of the code action lightbulb.
|
||||
*/
|
||||
@@ -644,15 +668,19 @@ export interface IDiffEditorOptions extends IEditorOptions {
|
||||
*/
|
||||
originalEditable?: boolean;
|
||||
/**
|
||||
* Original editor should be have code lens enabled?
|
||||
* Should the diff editor enable code lens?
|
||||
* Defaults to false.
|
||||
*/
|
||||
originalCodeLens?: boolean;
|
||||
diffCodeLens?: boolean;
|
||||
/**
|
||||
* Modified editor should be have code lens enabled?
|
||||
* Defaults to false.
|
||||
* Is the diff editor inside another editor
|
||||
* Defaults to false
|
||||
*/
|
||||
modifiedCodeLens?: boolean;
|
||||
isInEmbeddedEditor?: boolean;
|
||||
/**
|
||||
* Control the wrapping of the diff editor.
|
||||
*/
|
||||
diffWordWrap?: 'off' | 'on' | 'inherit';
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -828,18 +856,21 @@ class SimpleEditorOption<K1 extends EditorOption, V> implements IEditorOption<K1
|
||||
}
|
||||
}
|
||||
|
||||
class EditorBooleanOption<K1 extends EditorOption> extends SimpleEditorOption<K1, boolean> {
|
||||
|
||||
public static boolean(value: any, defaultValue: boolean): boolean {
|
||||
if (typeof value === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value === 'false') {
|
||||
// treat the string 'false' as false
|
||||
return false;
|
||||
}
|
||||
return Boolean(value);
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function boolean(value: any, defaultValue: boolean): boolean {
|
||||
if (typeof value === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value === 'false') {
|
||||
// treat the string 'false' as false
|
||||
return false;
|
||||
}
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
class EditorBooleanOption<K1 extends EditorOption> extends SimpleEditorOption<K1, boolean> {
|
||||
|
||||
constructor(id: K1, name: PossibleKeyName<boolean>, defaultValue: boolean, schema: IConfigurationPropertySchema | undefined = undefined) {
|
||||
if (typeof schema !== 'undefined') {
|
||||
@@ -850,7 +881,7 @@ class EditorBooleanOption<K1 extends EditorOption> extends SimpleEditorOption<K1
|
||||
}
|
||||
|
||||
public validate(input: any): boolean {
|
||||
return EditorBooleanOption.boolean(input, this.defaultValue);
|
||||
return boolean(input, this.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,17 +981,20 @@ class EditorStringOption<K1 extends EditorOption> extends SimpleEditorOption<K1,
|
||||
}
|
||||
}
|
||||
|
||||
class EditorStringEnumOption<K1 extends EditorOption, V extends string> extends SimpleEditorOption<K1, V> {
|
||||
|
||||
public static stringSet<T>(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray<T>): T {
|
||||
if (typeof value !== 'string') {
|
||||
return defaultValue;
|
||||
}
|
||||
if (allowedValues.indexOf(value) === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function stringSet<T>(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray<T>): T {
|
||||
if (typeof value !== 'string') {
|
||||
return defaultValue;
|
||||
}
|
||||
if (allowedValues.indexOf(value) === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
class EditorStringEnumOption<K1 extends EditorOption, V extends string> extends SimpleEditorOption<K1, V> {
|
||||
|
||||
private readonly _allowedValues: ReadonlyArray<V>;
|
||||
|
||||
@@ -975,7 +1009,7 @@ class EditorStringEnumOption<K1 extends EditorOption, V extends string> extends
|
||||
}
|
||||
|
||||
public validate(input: any): V {
|
||||
return EditorStringEnumOption.stringSet<V>(input, this.defaultValue, this._allowedValues);
|
||||
return stringSet<V>(input, this.defaultValue, this._allowedValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,8 +1147,8 @@ class EditorComments extends BaseEditorOption<EditorOption.comments, EditorComme
|
||||
}
|
||||
const input = _input as IEditorCommentsOptions;
|
||||
return {
|
||||
insertSpace: EditorBooleanOption.boolean(input.insertSpace, this.defaultValue.insertSpace),
|
||||
ignoreEmptyLines: EditorBooleanOption.boolean(input.ignoreEmptyLines, this.defaultValue.ignoreEmptyLines),
|
||||
insertSpace: boolean(input.insertSpace, this.defaultValue.insertSpace),
|
||||
ignoreEmptyLines: boolean(input.ignoreEmptyLines, this.defaultValue.ignoreEmptyLines),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1375,14 +1409,14 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
}
|
||||
const input = _input as IEditorFindOptions;
|
||||
return {
|
||||
cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType),
|
||||
seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection),
|
||||
cursorMoveOnType: boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType),
|
||||
seedSearchStringFromSelection: boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection),
|
||||
autoFindInSelection: typeof _input.autoFindInSelection === 'boolean'
|
||||
? (_input.autoFindInSelection ? 'always' : 'never')
|
||||
: EditorStringEnumOption.stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']),
|
||||
globalFindClipboard: EditorBooleanOption.boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard),
|
||||
addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop),
|
||||
loop: EditorBooleanOption.boolean(input.loop, this.defaultValue.loop),
|
||||
: stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']),
|
||||
globalFindClipboard: boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard),
|
||||
addExtraSpaceOnTop: boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop),
|
||||
loop: boolean(input.loop, this.defaultValue.loop),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1406,14 +1440,14 @@ export class EditorFontLigatures extends BaseEditorOption<EditorOption.fontLigat
|
||||
anyOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
description: nls.localize('fontLigatures', "Enables/Disables font ligatures."),
|
||||
description: nls.localize('fontLigatures', "Enables/Disables font ligatures ('calt' and 'liga' font features). Change this to a string for fine-grained control of the 'font-feature-settings' CSS property."),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: nls.localize('fontFeatureSettings', "Explicit font-feature-settings.")
|
||||
description: nls.localize('fontFeatureSettings', "Explicit 'font-feature-settings' CSS property. A boolean can be passed instead if one only needs to turn on/off ligatures.")
|
||||
}
|
||||
],
|
||||
description: nls.localize('fontLigaturesGeneral', "Configures font ligatures or font features."),
|
||||
description: nls.localize('fontLigaturesGeneral', "Configures font ligatures or font features. Can be either a boolean to enable/disable ligatures or a string for the value of the CSS 'font-feature-settings' property."),
|
||||
default: false
|
||||
}
|
||||
);
|
||||
@@ -1644,12 +1678,12 @@ class EditorGoToLocation extends BaseEditorOption<EditorOption.gotoLocation, GoT
|
||||
}
|
||||
const input = _input as IGotoLocationOptions;
|
||||
return {
|
||||
multiple: EditorStringEnumOption.stringSet<GoToLocationValues>(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleDefinitions: input.multipleDefinitions ?? EditorStringEnumOption.stringSet<GoToLocationValues>(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleTypeDefinitions: input.multipleTypeDefinitions ?? EditorStringEnumOption.stringSet<GoToLocationValues>(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleDeclarations: input.multipleDeclarations ?? EditorStringEnumOption.stringSet<GoToLocationValues>(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet<GoToLocationValues>(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet<GoToLocationValues>(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multiple: stringSet<GoToLocationValues>(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleDefinitions: input.multipleDefinitions ?? stringSet<GoToLocationValues>(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleTypeDefinitions: input.multipleTypeDefinitions ?? stringSet<GoToLocationValues>(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleDeclarations: input.multipleDeclarations ?? stringSet<GoToLocationValues>(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleImplementations: input.multipleImplementations ?? stringSet<GoToLocationValues>(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
multipleReferences: input.multipleReferences ?? stringSet<GoToLocationValues>(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']),
|
||||
alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand),
|
||||
alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand),
|
||||
alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand),
|
||||
@@ -1722,9 +1756,9 @@ class EditorHover extends BaseEditorOption<EditorOption.hover, EditorHoverOption
|
||||
}
|
||||
const input = _input as IEditorHoverOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
|
||||
enabled: boolean(input.enabled, this.defaultValue.enabled),
|
||||
delay: EditorIntOption.clampedInt(input.delay, this.defaultValue.delay, 0, 10000),
|
||||
sticky: EditorBooleanOption.boolean(input.sticky, this.defaultValue.sticky)
|
||||
sticky: boolean(input.sticky, this.defaultValue.sticky)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1928,7 +1962,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
EditorOption.glyphMargin, EditorOption.lineDecorationsWidth, EditorOption.folding,
|
||||
EditorOption.minimap, EditorOption.scrollbar, EditorOption.lineNumbers,
|
||||
EditorOption.lineNumbersMinChars, EditorOption.scrollBeyondLastLine,
|
||||
EditorOption.wordWrap, EditorOption.wordWrapColumn, EditorOption.wordWrapMinified,
|
||||
EditorOption.wordWrap, EditorOption.wordWrapColumn, EditorOption.wordWrapOverride1, EditorOption.wordWrapOverride2,
|
||||
EditorOption.accessibilitySupport
|
||||
]
|
||||
);
|
||||
@@ -2139,9 +2173,11 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
const pixelRatio = env.pixelRatio;
|
||||
const viewLineCount = env.viewLineCount;
|
||||
|
||||
const wordWrap = options.get(EditorOption.wordWrap);
|
||||
const wordWrapOverride2 = options.get(EditorOption.wordWrapOverride2);
|
||||
const wordWrapOverride1 = (wordWrapOverride2 === 'inherit' ? options.get(EditorOption.wordWrapOverride1) : wordWrapOverride2);
|
||||
const wordWrap = (wordWrapOverride1 === 'inherit' ? options.get(EditorOption.wordWrap) : wordWrapOverride1);
|
||||
|
||||
const wordWrapColumn = options.get(EditorOption.wordWrapColumn);
|
||||
const wordWrapMinified = options.get(EditorOption.wordWrapMinified);
|
||||
const accessibilitySupport = options.get(EditorOption.accessibilitySupport);
|
||||
const isDominatedByLongLines = env.isDominatedByLongLines;
|
||||
|
||||
@@ -2198,7 +2234,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
// Never enable wrapping when a screen reader is attached
|
||||
// because arrow down etc. will not move the cursor in the way
|
||||
// a screen reader expects.
|
||||
if (wordWrapMinified && isDominatedByLongLines) {
|
||||
if (wordWrapOverride1 === 'inherit' && isDominatedByLongLines) {
|
||||
// Force viewport width wrapping if model is dominated by long lines
|
||||
isWordWrapMinified = true;
|
||||
isViewportWrapping = true;
|
||||
@@ -2321,7 +2357,7 @@ class EditorLightbulb extends BaseEditorOption<EditorOption.lightbulb, EditorLig
|
||||
}
|
||||
const input = _input as IEditorLightbulbOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled)
|
||||
enabled: boolean(input.enabled, this.defaultValue.enabled)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2465,11 +2501,11 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
}
|
||||
const input = _input as IEditorMinimapOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
|
||||
size: EditorStringEnumOption.stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']),
|
||||
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
|
||||
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
enabled: boolean(input.enabled, this.defaultValue.enabled),
|
||||
size: stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']),
|
||||
side: stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
|
||||
showSlider: stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
scale: EditorIntOption.clampedInt(input.scale, 1, 1, 3),
|
||||
maxColumn: EditorIntOption.clampedInt(input.maxColumn, this.defaultValue.maxColumn, 1, 10000),
|
||||
};
|
||||
@@ -2598,8 +2634,8 @@ class EditorParameterHints extends BaseEditorOption<EditorOption.parameterHints,
|
||||
}
|
||||
const input = _input as IEditorParameterHintOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
|
||||
cycle: EditorBooleanOption.boolean(input.cycle, this.defaultValue.cycle)
|
||||
enabled: boolean(input.enabled, this.defaultValue.enabled),
|
||||
cycle: boolean(input.cycle, this.defaultValue.cycle)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2686,9 +2722,9 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
|
||||
if (_input && typeof _input === 'object') {
|
||||
const input = _input as IQuickSuggestionsOptions;
|
||||
const opts = {
|
||||
other: EditorBooleanOption.boolean(input.other, this.defaultValue.other),
|
||||
comments: EditorBooleanOption.boolean(input.comments, this.defaultValue.comments),
|
||||
strings: EditorBooleanOption.boolean(input.strings, this.defaultValue.strings),
|
||||
other: boolean(input.other, this.defaultValue.other),
|
||||
comments: boolean(input.comments, this.defaultValue.comments),
|
||||
strings: boolean(input.strings, this.defaultValue.strings),
|
||||
};
|
||||
if (opts.other && opts.comments && opts.strings) {
|
||||
return true; // all on
|
||||
@@ -2916,6 +2952,11 @@ export interface IEditorScrollbarOptions {
|
||||
* Defaults to `horizontalScrollbarSize`.
|
||||
*/
|
||||
horizontalSliderSize?: number;
|
||||
/**
|
||||
* Scroll gutter clicks move by page vs jump to position.
|
||||
* Defaults to false.
|
||||
*/
|
||||
scrollByPage?: boolean;
|
||||
}
|
||||
|
||||
export interface InternalEditorScrollbarOptions {
|
||||
@@ -2931,6 +2972,7 @@ export interface InternalEditorScrollbarOptions {
|
||||
readonly horizontalSliderSize: number;
|
||||
readonly verticalScrollbarSize: number;
|
||||
readonly verticalSliderSize: number;
|
||||
readonly scrollByPage: boolean;
|
||||
}
|
||||
|
||||
function _scrollbarVisibilityFromString(visibility: string | undefined, defaultValue: ScrollbarVisibility): ScrollbarVisibility {
|
||||
@@ -2961,7 +3003,8 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
|
||||
verticalScrollbarSize: 14,
|
||||
verticalSliderSize: 14,
|
||||
handleMouseWheel: true,
|
||||
alwaysConsumeMouseWheel: true
|
||||
alwaysConsumeMouseWheel: true,
|
||||
scrollByPage: false
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2977,15 +3020,16 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
|
||||
arrowSize: EditorIntOption.clampedInt(input.arrowSize, this.defaultValue.arrowSize, 0, 1000),
|
||||
vertical: _scrollbarVisibilityFromString(input.vertical, this.defaultValue.vertical),
|
||||
horizontal: _scrollbarVisibilityFromString(input.horizontal, this.defaultValue.horizontal),
|
||||
useShadows: EditorBooleanOption.boolean(input.useShadows, this.defaultValue.useShadows),
|
||||
verticalHasArrows: EditorBooleanOption.boolean(input.verticalHasArrows, this.defaultValue.verticalHasArrows),
|
||||
horizontalHasArrows: EditorBooleanOption.boolean(input.horizontalHasArrows, this.defaultValue.horizontalHasArrows),
|
||||
handleMouseWheel: EditorBooleanOption.boolean(input.handleMouseWheel, this.defaultValue.handleMouseWheel),
|
||||
alwaysConsumeMouseWheel: EditorBooleanOption.boolean(input.alwaysConsumeMouseWheel, this.defaultValue.alwaysConsumeMouseWheel),
|
||||
useShadows: boolean(input.useShadows, this.defaultValue.useShadows),
|
||||
verticalHasArrows: boolean(input.verticalHasArrows, this.defaultValue.verticalHasArrows),
|
||||
horizontalHasArrows: boolean(input.horizontalHasArrows, this.defaultValue.horizontalHasArrows),
|
||||
handleMouseWheel: boolean(input.handleMouseWheel, this.defaultValue.handleMouseWheel),
|
||||
alwaysConsumeMouseWheel: boolean(input.alwaysConsumeMouseWheel, this.defaultValue.alwaysConsumeMouseWheel),
|
||||
horizontalScrollbarSize: horizontalScrollbarSize,
|
||||
horizontalSliderSize: EditorIntOption.clampedInt(input.horizontalSliderSize, horizontalScrollbarSize, 0, 1000),
|
||||
verticalScrollbarSize: verticalScrollbarSize,
|
||||
verticalSliderSize: EditorIntOption.clampedInt(input.verticalSliderSize, verticalScrollbarSize, 0, 1000),
|
||||
scrollByPage: boolean(input.scrollByPage, this.defaultValue.scrollByPage),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3026,6 +3070,10 @@ export interface ISuggestOptions {
|
||||
* Enable or disable the suggest status bar.
|
||||
*/
|
||||
showStatusBar?: boolean;
|
||||
/**
|
||||
* Show details inline with the label. Defaults to true.
|
||||
*/
|
||||
showInlineDetails?: boolean;
|
||||
/**
|
||||
* Show method-suggestions.
|
||||
*/
|
||||
@@ -3149,6 +3197,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
shareSuggestSelections: false,
|
||||
showIcons: true,
|
||||
showStatusBar: false,
|
||||
showInlineDetails: true,
|
||||
showMethods: true,
|
||||
showFunctions: true,
|
||||
showConstructors: true,
|
||||
@@ -3220,6 +3269,12 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
default: defaults.showStatusBar,
|
||||
description: nls.localize('suggest.showStatusBar', "Controls the visibility of the status bar at the bottom of the suggest widget.")
|
||||
},
|
||||
|
||||
'editor.suggest.showInlineDetails': {
|
||||
type: 'boolean',
|
||||
default: defaults.showInlineDetails,
|
||||
description: nls.localize('suggest.showInlineDetails', "Controls whether sugget details show inline with the label or only in the details widget")
|
||||
},
|
||||
'editor.suggest.maxVisibleSuggestions': {
|
||||
type: 'number',
|
||||
deprecationMessage: nls.localize('suggest.maxVisibleSuggestions.dep', "This setting is deprecated. The suggest widget can now be resized."),
|
||||
@@ -3378,40 +3433,79 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
}
|
||||
const input = _input as ISuggestOptions;
|
||||
return {
|
||||
insertMode: EditorStringEnumOption.stringSet(input.insertMode, this.defaultValue.insertMode, ['insert', 'replace']),
|
||||
filterGraceful: EditorBooleanOption.boolean(input.filterGraceful, this.defaultValue.filterGraceful),
|
||||
snippetsPreventQuickSuggestions: EditorBooleanOption.boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
|
||||
localityBonus: EditorBooleanOption.boolean(input.localityBonus, this.defaultValue.localityBonus),
|
||||
shareSuggestSelections: EditorBooleanOption.boolean(input.shareSuggestSelections, this.defaultValue.shareSuggestSelections),
|
||||
showIcons: EditorBooleanOption.boolean(input.showIcons, this.defaultValue.showIcons),
|
||||
showStatusBar: EditorBooleanOption.boolean(input.showStatusBar, this.defaultValue.showStatusBar),
|
||||
showMethods: EditorBooleanOption.boolean(input.showMethods, this.defaultValue.showMethods),
|
||||
showFunctions: EditorBooleanOption.boolean(input.showFunctions, this.defaultValue.showFunctions),
|
||||
showConstructors: EditorBooleanOption.boolean(input.showConstructors, this.defaultValue.showConstructors),
|
||||
showFields: EditorBooleanOption.boolean(input.showFields, this.defaultValue.showFields),
|
||||
showVariables: EditorBooleanOption.boolean(input.showVariables, this.defaultValue.showVariables),
|
||||
showClasses: EditorBooleanOption.boolean(input.showClasses, this.defaultValue.showClasses),
|
||||
showStructs: EditorBooleanOption.boolean(input.showStructs, this.defaultValue.showStructs),
|
||||
showInterfaces: EditorBooleanOption.boolean(input.showInterfaces, this.defaultValue.showInterfaces),
|
||||
showModules: EditorBooleanOption.boolean(input.showModules, this.defaultValue.showModules),
|
||||
showProperties: EditorBooleanOption.boolean(input.showProperties, this.defaultValue.showProperties),
|
||||
showEvents: EditorBooleanOption.boolean(input.showEvents, this.defaultValue.showEvents),
|
||||
showOperators: EditorBooleanOption.boolean(input.showOperators, this.defaultValue.showOperators),
|
||||
showUnits: EditorBooleanOption.boolean(input.showUnits, this.defaultValue.showUnits),
|
||||
showValues: EditorBooleanOption.boolean(input.showValues, this.defaultValue.showValues),
|
||||
showConstants: EditorBooleanOption.boolean(input.showConstants, this.defaultValue.showConstants),
|
||||
showEnums: EditorBooleanOption.boolean(input.showEnums, this.defaultValue.showEnums),
|
||||
showEnumMembers: EditorBooleanOption.boolean(input.showEnumMembers, this.defaultValue.showEnumMembers),
|
||||
showKeywords: EditorBooleanOption.boolean(input.showKeywords, this.defaultValue.showKeywords),
|
||||
showWords: EditorBooleanOption.boolean(input.showWords, this.defaultValue.showWords),
|
||||
showColors: EditorBooleanOption.boolean(input.showColors, this.defaultValue.showColors),
|
||||
showFiles: EditorBooleanOption.boolean(input.showFiles, this.defaultValue.showFiles),
|
||||
showReferences: EditorBooleanOption.boolean(input.showReferences, this.defaultValue.showReferences),
|
||||
showFolders: EditorBooleanOption.boolean(input.showFolders, this.defaultValue.showFolders),
|
||||
showTypeParameters: EditorBooleanOption.boolean(input.showTypeParameters, this.defaultValue.showTypeParameters),
|
||||
showSnippets: EditorBooleanOption.boolean(input.showSnippets, this.defaultValue.showSnippets),
|
||||
showUsers: EditorBooleanOption.boolean(input.showUsers, this.defaultValue.showUsers),
|
||||
showIssues: EditorBooleanOption.boolean(input.showIssues, this.defaultValue.showIssues),
|
||||
insertMode: stringSet(input.insertMode, this.defaultValue.insertMode, ['insert', 'replace']),
|
||||
filterGraceful: boolean(input.filterGraceful, this.defaultValue.filterGraceful),
|
||||
snippetsPreventQuickSuggestions: boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
|
||||
localityBonus: boolean(input.localityBonus, this.defaultValue.localityBonus),
|
||||
shareSuggestSelections: boolean(input.shareSuggestSelections, this.defaultValue.shareSuggestSelections),
|
||||
showIcons: boolean(input.showIcons, this.defaultValue.showIcons),
|
||||
showStatusBar: boolean(input.showStatusBar, this.defaultValue.showStatusBar),
|
||||
showInlineDetails: boolean(input.showInlineDetails, this.defaultValue.showInlineDetails),
|
||||
showMethods: boolean(input.showMethods, this.defaultValue.showMethods),
|
||||
showFunctions: boolean(input.showFunctions, this.defaultValue.showFunctions),
|
||||
showConstructors: boolean(input.showConstructors, this.defaultValue.showConstructors),
|
||||
showFields: boolean(input.showFields, this.defaultValue.showFields),
|
||||
showVariables: boolean(input.showVariables, this.defaultValue.showVariables),
|
||||
showClasses: boolean(input.showClasses, this.defaultValue.showClasses),
|
||||
showStructs: boolean(input.showStructs, this.defaultValue.showStructs),
|
||||
showInterfaces: boolean(input.showInterfaces, this.defaultValue.showInterfaces),
|
||||
showModules: boolean(input.showModules, this.defaultValue.showModules),
|
||||
showProperties: boolean(input.showProperties, this.defaultValue.showProperties),
|
||||
showEvents: boolean(input.showEvents, this.defaultValue.showEvents),
|
||||
showOperators: boolean(input.showOperators, this.defaultValue.showOperators),
|
||||
showUnits: boolean(input.showUnits, this.defaultValue.showUnits),
|
||||
showValues: boolean(input.showValues, this.defaultValue.showValues),
|
||||
showConstants: boolean(input.showConstants, this.defaultValue.showConstants),
|
||||
showEnums: boolean(input.showEnums, this.defaultValue.showEnums),
|
||||
showEnumMembers: boolean(input.showEnumMembers, this.defaultValue.showEnumMembers),
|
||||
showKeywords: boolean(input.showKeywords, this.defaultValue.showKeywords),
|
||||
showWords: boolean(input.showWords, this.defaultValue.showWords),
|
||||
showColors: boolean(input.showColors, this.defaultValue.showColors),
|
||||
showFiles: boolean(input.showFiles, this.defaultValue.showFiles),
|
||||
showReferences: boolean(input.showReferences, this.defaultValue.showReferences),
|
||||
showFolders: boolean(input.showFolders, this.defaultValue.showFolders),
|
||||
showTypeParameters: boolean(input.showTypeParameters, this.defaultValue.showTypeParameters),
|
||||
showSnippets: boolean(input.showSnippets, this.defaultValue.showSnippets),
|
||||
showUsers: boolean(input.showUsers, this.defaultValue.showUsers),
|
||||
showIssues: boolean(input.showIssues, this.defaultValue.showIssues),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region smart select
|
||||
|
||||
export interface ISmartSelectOptions {
|
||||
selectLeadingAndTrailingWhitespace?: boolean
|
||||
}
|
||||
|
||||
export type SmartSelectOptions = Readonly<Required<ISmartSelectOptions>>;
|
||||
|
||||
class SmartSelect extends BaseEditorOption<EditorOption.smartSelect, SmartSelectOptions> {
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
EditorOption.smartSelect, 'smartSelect',
|
||||
{
|
||||
selectLeadingAndTrailingWhitespace: true
|
||||
},
|
||||
{
|
||||
'editor.smartSelect.selectLeadingAndTrailingWhitespace': {
|
||||
description: nls.localize('selectLeadingAndTrailingWhitespace', "Whether leading and trailing whitespace should always be selected."),
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public validate(input: any): Readonly<Required<ISmartSelectOptions>> {
|
||||
if (!input || typeof input !== 'object') {
|
||||
return this.defaultValue;
|
||||
}
|
||||
return {
|
||||
selectLeadingAndTrailingWhitespace: boolean((input as ISmartSelectOptions).selectLeadingAndTrailingWhitespace, this.defaultValue.selectLeadingAndTrailingWhitespace)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3552,6 +3646,8 @@ export const enum EditorOption {
|
||||
automaticLayout,
|
||||
autoSurround,
|
||||
codeLens,
|
||||
codeLensFontFamily,
|
||||
codeLensFontSize,
|
||||
colorDecorators,
|
||||
columnSelection,
|
||||
comments,
|
||||
@@ -3594,6 +3690,7 @@ export const enum EditorOption {
|
||||
lineHeight,
|
||||
lineNumbers,
|
||||
lineNumbersMinChars,
|
||||
linkedEditing,
|
||||
links,
|
||||
matchBrackets,
|
||||
minimap,
|
||||
@@ -3634,7 +3731,9 @@ export const enum EditorOption {
|
||||
showFoldingControls,
|
||||
showUnused,
|
||||
snippetSuggestions,
|
||||
smartSelect,
|
||||
smoothScrolling,
|
||||
stickyTabStops,
|
||||
stopRenderingLineAfter,
|
||||
suggest,
|
||||
suggestFontSize,
|
||||
@@ -3650,7 +3749,8 @@ export const enum EditorOption {
|
||||
wordWrapBreakAfterCharacters,
|
||||
wordWrapBreakBeforeCharacters,
|
||||
wordWrapColumn,
|
||||
wordWrapMinified,
|
||||
wordWrapOverride1,
|
||||
wordWrapOverride2,
|
||||
wrappingIndent,
|
||||
wrappingStrategy,
|
||||
showDeprecated,
|
||||
@@ -3776,10 +3876,25 @@ export const EditorOptions = {
|
||||
description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.")
|
||||
}
|
||||
)),
|
||||
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.") }
|
||||
)),
|
||||
codeLens: register(new EditorBooleanOption(
|
||||
EditorOption.codeLens, 'codeLens', true,
|
||||
{ description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") }
|
||||
)),
|
||||
codeLensFontFamily: register(new EditorStringOption(
|
||||
EditorOption.codeLensFontFamily, 'codeLensFontFamily', '',
|
||||
{ description: nls.localize('codeLensFontFamily', "Controls the font family for CodeLens.") }
|
||||
)),
|
||||
codeLensFontSize: register(new EditorIntOption(EditorOption.codeLensFontSize, 'codeLensFontSize', 0, 0, 100, {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
minimum: 0,
|
||||
maximum: 100,
|
||||
description: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.")
|
||||
})),
|
||||
colorDecorators: register(new EditorBooleanOption(
|
||||
EditorOption.colorDecorators, 'colorDecorators', true,
|
||||
{ description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") }
|
||||
@@ -3914,7 +4029,7 @@ export const EditorOptions = {
|
||||
)),
|
||||
hover: register(new EditorHover()),
|
||||
inDiffEditor: register(new EditorBooleanOption(
|
||||
EditorOption.inDiffEditor, 'inDiffEditor', false,
|
||||
EditorOption.inDiffEditor, 'inDiffEditor', false
|
||||
)),
|
||||
letterSpacing: register(new EditorFloatOption(
|
||||
EditorOption.letterSpacing, 'letterSpacing',
|
||||
@@ -3929,6 +4044,10 @@ export const EditorOptions = {
|
||||
EditorOption.lineNumbersMinChars, 'lineNumbersMinChars',
|
||||
5, 1, 300
|
||||
)),
|
||||
linkedEditing: register(new EditorBooleanOption(
|
||||
EditorOption.linkedEditing, 'linkedEditing', false,
|
||||
{ description: nls.localize('linkedEditing', "Controls whether the editor has linked editing enabled. Depending on the language, related symbols, e.g. HTML tags, are updated while editing.") }
|
||||
)),
|
||||
links: register(new EditorBooleanOption(
|
||||
EditorOption.links, 'links', true,
|
||||
{ description: nls.localize('links', "Controls whether the editor should detect links and make them clickable.") }
|
||||
@@ -4030,7 +4149,7 @@ export const EditorOptions = {
|
||||
)),
|
||||
renameOnType: register(new EditorBooleanOption(
|
||||
EditorOption.renameOnType, 'renameOnType', false,
|
||||
{ description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") }
|
||||
{ description: nls.localize('renameOnType', "Controls whether the editor auto renames on type."), markdownDeprecationMessage: nls.localize('renameOnTypeDeprecate', "Deprecated, use `editor.linkedEditing` instead.") }
|
||||
)),
|
||||
renderControlCharacters: register(new EditorBooleanOption(
|
||||
EditorOption.renderControlCharacters, 'renderControlCharacters', false,
|
||||
@@ -4153,6 +4272,7 @@ export const EditorOptions = {
|
||||
description: nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.")
|
||||
}
|
||||
)),
|
||||
smartSelect: register(new SmartSelect()),
|
||||
smoothScrolling: register(new EditorBooleanOption(
|
||||
EditorOption.smoothScrolling, 'smoothScrolling', false,
|
||||
{ description: nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.") }
|
||||
@@ -4170,7 +4290,7 @@ export const EditorOptions = {
|
||||
suggestLineHeight: register(new EditorIntOption(
|
||||
EditorOption.suggestLineHeight, 'suggestLineHeight',
|
||||
0, 0, 1000,
|
||||
{ markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used.") }
|
||||
{ markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used. The minimum value is 8.") }
|
||||
)),
|
||||
suggestOnTriggerCharacters: register(new EditorBooleanOption(
|
||||
EditorOption.suggestOnTriggerCharacters, 'suggestOnTriggerCharacters', true,
|
||||
@@ -4279,8 +4399,15 @@ export const EditorOptions = {
|
||||
}, "Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.")
|
||||
}
|
||||
)),
|
||||
wordWrapMinified: register(new EditorBooleanOption(
|
||||
EditorOption.wordWrapMinified, 'wordWrapMinified', true,
|
||||
wordWrapOverride1: register(new EditorStringEnumOption(
|
||||
EditorOption.wordWrapOverride1, 'wordWrapOverride1',
|
||||
'inherit' as 'off' | 'on' | 'inherit',
|
||||
['off', 'on', 'inherit'] as const
|
||||
)),
|
||||
wordWrapOverride2: register(new EditorStringEnumOption(
|
||||
EditorOption.wordWrapOverride2, 'wordWrapOverride2',
|
||||
'inherit' as 'off' | 'on' | 'inherit',
|
||||
['off', 'on', 'inherit'] as const
|
||||
)),
|
||||
wrappingIndent: register(new EditorEnumOption(
|
||||
EditorOption.wrappingIndent, 'wrappingIndent',
|
||||
|
||||
@@ -531,7 +531,7 @@ export class Cursor extends Disposable {
|
||||
}
|
||||
const closeChar = m[1];
|
||||
|
||||
const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar);
|
||||
const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairs.autoClosingPairsCloseSingleChar.get(closeChar);
|
||||
if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
|
||||
|
||||
export const enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
Nearest,
|
||||
}
|
||||
|
||||
export class AtomicTabMoveOperations {
|
||||
/**
|
||||
* Get the visible column at the position. If we get to a non-whitespace character first
|
||||
* or past the end of string then return -1.
|
||||
*
|
||||
* **Note** `position` and the return value are 0-based.
|
||||
*/
|
||||
public static whitespaceVisibleColumn(lineContent: string, position: number, tabSize: number): [number, number, number] {
|
||||
const lineLength = lineContent.length;
|
||||
let visibleColumn = 0;
|
||||
let prevTabStopPosition = -1;
|
||||
let prevTabStopVisibleColumn = -1;
|
||||
for (let i = 0; i < lineLength; i++) {
|
||||
if (i === position) {
|
||||
return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn];
|
||||
}
|
||||
if (visibleColumn % tabSize === 0) {
|
||||
prevTabStopPosition = i;
|
||||
prevTabStopVisibleColumn = visibleColumn;
|
||||
}
|
||||
const chCode = lineContent.charCodeAt(i);
|
||||
switch (chCode) {
|
||||
case CharCode.Space:
|
||||
visibleColumn += 1;
|
||||
break;
|
||||
case CharCode.Tab:
|
||||
// Skip to the next multiple of tabSize.
|
||||
visibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize);
|
||||
break;
|
||||
default:
|
||||
return [-1, -1, -1];
|
||||
}
|
||||
}
|
||||
if (position === lineLength) {
|
||||
return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn];
|
||||
}
|
||||
return [-1, -1, -1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position that should result from a move left, right or to the
|
||||
* nearest tab, if atomic tabs are enabled. Left and right are used for the
|
||||
* arrow key movements, nearest is used for mouse selection. It returns
|
||||
* -1 if atomic tabs are not relevant and you should fall back to normal
|
||||
* behaviour.
|
||||
*
|
||||
* **Note**: `position` and the return value are 0-based.
|
||||
*/
|
||||
public static atomicPosition(lineContent: string, position: number, tabSize: number, direction: Direction): number {
|
||||
const lineLength = lineContent.length;
|
||||
|
||||
// Get the 0-based visible column corresponding to the position, or return
|
||||
// -1 if it is not in the initial whitespace.
|
||||
const [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn] = AtomicTabMoveOperations.whitespaceVisibleColumn(lineContent, position, tabSize);
|
||||
|
||||
if (visibleColumn === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Is the output left or right of the current position. The case for nearest
|
||||
// where it is the same as the current position is handled in the switch.
|
||||
let left: boolean;
|
||||
switch (direction) {
|
||||
case Direction.Left:
|
||||
left = true;
|
||||
break;
|
||||
case Direction.Right:
|
||||
left = false;
|
||||
break;
|
||||
case Direction.Nearest:
|
||||
// The code below assumes the output position is either left or right
|
||||
// of the input position. If it is the same, return immediately.
|
||||
if (visibleColumn % tabSize === 0) {
|
||||
return position;
|
||||
}
|
||||
// Go to the nearest indentation.
|
||||
left = visibleColumn % tabSize <= (tabSize / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
// If going left, we can just use the info about the last tab stop position and
|
||||
// last tab stop visible column that we computed in the first walk over the whitespace.
|
||||
if (left) {
|
||||
if (prevTabStopPosition === -1) {
|
||||
return -1;
|
||||
}
|
||||
// If the direction is left, we need to keep scanning right to ensure
|
||||
// that targetVisibleColumn + tabSize is before non-whitespace.
|
||||
// This is so that when we press left at the end of a partial
|
||||
// indentation it only goes one character. For example ' foo' with
|
||||
// tabSize 4, should jump from position 6 to position 5, not 4.
|
||||
let currentVisibleColumn = prevTabStopVisibleColumn;
|
||||
for (let i = prevTabStopPosition; i < lineLength; ++i) {
|
||||
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
|
||||
// It is a full indentation.
|
||||
return prevTabStopPosition;
|
||||
}
|
||||
|
||||
const chCode = lineContent.charCodeAt(i);
|
||||
switch (chCode) {
|
||||
case CharCode.Space:
|
||||
currentVisibleColumn += 1;
|
||||
break;
|
||||
case CharCode.Tab:
|
||||
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
|
||||
return prevTabStopPosition;
|
||||
}
|
||||
// It must have been a partial indentation.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We are going right.
|
||||
const targetVisibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize);
|
||||
|
||||
// We can just continue from where whitespaceVisibleColumn got to.
|
||||
let currentVisibleColumn = visibleColumn;
|
||||
for (let i = position; i < lineLength; i++) {
|
||||
if (currentVisibleColumn === targetVisibleColumn) {
|
||||
return i;
|
||||
}
|
||||
|
||||
const chCode = lineContent.charCodeAt(i);
|
||||
switch (chCode) {
|
||||
case CharCode.Space:
|
||||
currentVisibleColumn += 1;
|
||||
break;
|
||||
case CharCode.Tab:
|
||||
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// This condition handles when the target column is at the end of the line.
|
||||
if (currentVisibleColumn === targetVisibleColumn) {
|
||||
return lineLength;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { AutoClosingPairs, IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
@@ -62,6 +62,7 @@ export class CursorConfiguration {
|
||||
public readonly tabSize: number;
|
||||
public readonly indentSize: number;
|
||||
public readonly insertSpaces: boolean;
|
||||
public readonly stickyTabStops: boolean;
|
||||
public readonly pageSize: number;
|
||||
public readonly lineHeight: number;
|
||||
public readonly useTabStops: boolean;
|
||||
@@ -75,8 +76,7 @@ export class CursorConfiguration {
|
||||
public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy;
|
||||
public readonly autoSurround: EditorAutoSurroundStrategy;
|
||||
public readonly autoIndent: EditorAutoIndentStrategy;
|
||||
public readonly autoClosingPairsOpen2: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
public readonly autoClosingPairsClose2: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
public readonly autoClosingPairs: AutoClosingPairs;
|
||||
public readonly surroundingPairs: CharacterMap;
|
||||
public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean };
|
||||
|
||||
@@ -114,6 +114,7 @@ export class CursorConfiguration {
|
||||
this.tabSize = modelOptions.tabSize;
|
||||
this.indentSize = modelOptions.indentSize;
|
||||
this.insertSpaces = modelOptions.insertSpaces;
|
||||
this.stickyTabStops = options.get(EditorOption.stickyTabStops);
|
||||
this.lineHeight = options.get(EditorOption.lineHeight);
|
||||
this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2);
|
||||
this.useTabStops = options.get(EditorOption.useTabStops);
|
||||
@@ -136,9 +137,7 @@ export class CursorConfiguration {
|
||||
bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets)
|
||||
};
|
||||
|
||||
const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id);
|
||||
this.autoClosingPairsOpen2 = autoClosingPairs.autoClosingPairsOpen;
|
||||
this.autoClosingPairsClose2 = autoClosingPairs.autoClosingPairsClose;
|
||||
this.autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id);
|
||||
|
||||
let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier);
|
||||
if (surroundingPairs) {
|
||||
@@ -557,14 +556,14 @@ export class CursorColumns {
|
||||
}
|
||||
|
||||
/**
|
||||
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
|
||||
* ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns)
|
||||
*/
|
||||
public static prevRenderTabStop(column: number, tabSize: number): number {
|
||||
return column - 1 - (column - 1) % tabSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
|
||||
* ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns)
|
||||
*/
|
||||
public static prevIndentTabStop(column: number, indentSize: number): number {
|
||||
return column - 1 - (column - 1) % indentSize;
|
||||
|
||||
@@ -122,7 +122,7 @@ export class DeleteOperations {
|
||||
|
||||
public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array<ICommand | null>] {
|
||||
|
||||
if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairsOpen2, model, selections)) {
|
||||
if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections)) {
|
||||
return this._runAutoClosingPairDelete(config, model, selections);
|
||||
}
|
||||
|
||||
|
||||
@@ -412,7 +412,11 @@ export class CursorMoveCommands {
|
||||
const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
|
||||
let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
|
||||
|
||||
if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
|
||||
if (skipWrappingPointStop
|
||||
&& noOfColumns === 1
|
||||
&& cursor.viewState.position.column === viewModel.getLineMinColumn(cursor.viewState.position.lineNumber)
|
||||
&& newViewState.position.lineNumber !== cursor.viewState.position.lineNumber
|
||||
) {
|
||||
// moved over to the previous view line
|
||||
const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
|
||||
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
|
||||
@@ -445,7 +449,11 @@ export class CursorMoveCommands {
|
||||
const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
|
||||
let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
|
||||
|
||||
if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
|
||||
if (skipWrappingPointStop
|
||||
&& noOfColumns === 1
|
||||
&& cursor.viewState.position.column === viewModel.getLineMaxColumn(cursor.viewState.position.lineNumber)
|
||||
&& newViewState.position.lineNumber !== cursor.viewState.position.lineNumber
|
||||
) {
|
||||
// moved over to the next view line
|
||||
const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
|
||||
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations';
|
||||
|
||||
export class CursorPosition {
|
||||
_cursorPositionBrand: void;
|
||||
@@ -35,8 +36,20 @@ export class MoveOperations {
|
||||
return new Position(lineNumber, column);
|
||||
}
|
||||
|
||||
public static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number): Position {
|
||||
const minColumn = model.getLineMinColumn(lineNumber);
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Left);
|
||||
if (newPosition === -1) {
|
||||
return this.leftPosition(model, lineNumber, column);
|
||||
}
|
||||
return new Position(lineNumber, minColumn + newPosition);
|
||||
}
|
||||
|
||||
public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
|
||||
const pos = MoveOperations.leftPosition(model, lineNumber, column);
|
||||
const pos = config.stickyTabStops
|
||||
? MoveOperations.leftPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize)
|
||||
: MoveOperations.leftPosition(model, lineNumber, column);
|
||||
return new CursorPosition(pos.lineNumber, pos.column, 0);
|
||||
}
|
||||
|
||||
@@ -67,8 +80,20 @@ export class MoveOperations {
|
||||
return new Position(lineNumber, column);
|
||||
}
|
||||
|
||||
public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position {
|
||||
const minColumn = model.getLineMinColumn(lineNumber);
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Right);
|
||||
if (newPosition === -1) {
|
||||
return this.rightPosition(model, lineNumber, column);
|
||||
}
|
||||
return new Position(lineNumber, minColumn + newPosition);
|
||||
}
|
||||
|
||||
public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
|
||||
const pos = MoveOperations.rightPosition(model, lineNumber, column);
|
||||
const pos = config.stickyTabStops
|
||||
? MoveOperations.rightPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize, config.indentSize)
|
||||
: MoveOperations.rightPosition(model, lineNumber, column);
|
||||
return new CursorPosition(pos.lineNumber, pos.column, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export class TypeOperations {
|
||||
if (text.charCodeAt(text.length - 1) === CharCode.CarriageReturn) {
|
||||
text = text.substr(0, text.length - 1);
|
||||
}
|
||||
let lines = text.split(/\r\n|\r|\n/);
|
||||
let lines = strings.splitLines(text);
|
||||
if (lines.length === selections.length) {
|
||||
return lines;
|
||||
}
|
||||
@@ -439,7 +439,7 @@ export class TypeOperations {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.autoClosingPairsClose2.has(ch)) {
|
||||
if (!config.autoClosingPairs.autoClosingPairsCloseSingleChar.has(ch)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -498,31 +498,20 @@ export class TypeOperations {
|
||||
});
|
||||
}
|
||||
|
||||
private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean {
|
||||
const { open, close } = autoClosingPair;
|
||||
return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0);
|
||||
}
|
||||
private static _isBeforeClosingBrace(config: CursorConfiguration, lineAfter: string) {
|
||||
// If the start of lineAfter can be interpretted as both a starting or ending brace, default to returning false
|
||||
const nextChar = lineAfter.charAt(0);
|
||||
const potentialStartingBraces = config.autoClosingPairs.autoClosingPairsOpenByStart.get(nextChar) || [];
|
||||
const potentialClosingBraces = config.autoClosingPairs.autoClosingPairsCloseByStart.get(nextChar) || [];
|
||||
|
||||
private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) {
|
||||
const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter);
|
||||
if (!otherAutoClosingPairs) {
|
||||
return false;
|
||||
}
|
||||
const isBeforeStartingBrace = potentialStartingBraces.some(x => lineAfter.startsWith(x.open));
|
||||
const isBeforeClosingBrace = potentialClosingBraces.some(x => lineAfter.startsWith(x.close));
|
||||
|
||||
const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair);
|
||||
for (const otherAutoClosingPair of otherAutoClosingPairs) {
|
||||
const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair);
|
||||
if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !isBeforeStartingBrace && isBeforeClosingBrace;
|
||||
}
|
||||
|
||||
private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null {
|
||||
const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(ch);
|
||||
const autoClosingPairCandidates = config.autoClosingPairs.autoClosingPairsOpenByEnd.get(ch);
|
||||
if (!autoClosingPairCandidates) {
|
||||
return null;
|
||||
}
|
||||
@@ -548,7 +537,29 @@ export class TypeOperations {
|
||||
return autoClosingPair;
|
||||
}
|
||||
|
||||
private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): StandardAutoClosingPairConditional | null {
|
||||
private static _findSubAutoClosingPairClose(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional): string {
|
||||
if (autoClosingPair.open.length <= 1) {
|
||||
return '';
|
||||
}
|
||||
const lastChar = autoClosingPair.close.charAt(autoClosingPair.close.length - 1);
|
||||
// get candidates with the same last character as close
|
||||
const subPairCandidates = config.autoClosingPairs.autoClosingPairsCloseByEnd.get(lastChar) || [];
|
||||
let subPairMatch: StandardAutoClosingPairConditional | null = null;
|
||||
for (const x of subPairCandidates) {
|
||||
if (x.open !== autoClosingPair.open && autoClosingPair.open.includes(x.open) && autoClosingPair.close.endsWith(x.close)) {
|
||||
if (!subPairMatch || x.open.length > subPairMatch.open.length) {
|
||||
subPairMatch = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (subPairMatch) {
|
||||
return subPairMatch.close;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null {
|
||||
const chIsQuote = isQuote(ch);
|
||||
const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets;
|
||||
if (autoCloseConfig === 'never') {
|
||||
@@ -560,6 +571,9 @@ export class TypeOperations {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subAutoClosingPairClose = this._findSubAutoClosingPairClose(config, autoClosingPair);
|
||||
let isSubAutoClosingPairPresent = true;
|
||||
|
||||
const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket;
|
||||
|
||||
for (let i = 0, len = selections.length; i < len; i++) {
|
||||
@@ -570,11 +584,16 @@ export class TypeOperations {
|
||||
|
||||
const position = selection.getPosition();
|
||||
const lineText = model.getLineContent(position.lineNumber);
|
||||
const lineAfter = lineText.substring(position.column - 1);
|
||||
|
||||
// Only consider auto closing the pair if a space follows or if another autoclosed pair follows
|
||||
if (!lineAfter.startsWith(subAutoClosingPairClose)) {
|
||||
isSubAutoClosingPairPresent = false;
|
||||
}
|
||||
|
||||
// Only consider auto closing the pair if an allowed character follows or if another autoclosed pair closing brace follows
|
||||
if (lineText.length > position.column - 1) {
|
||||
const characterAfter = lineText.charAt(position.column - 1);
|
||||
const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, autoClosingPair, characterAfter);
|
||||
const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, lineAfter);
|
||||
|
||||
if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) {
|
||||
return null;
|
||||
@@ -612,14 +631,18 @@ export class TypeOperations {
|
||||
}
|
||||
}
|
||||
|
||||
return autoClosingPair;
|
||||
if (isSubAutoClosingPairPresent) {
|
||||
return autoClosingPair.close.substring(0, autoClosingPair.close.length - subAutoClosingPairClose.length);
|
||||
} else {
|
||||
return autoClosingPair.close;
|
||||
}
|
||||
}
|
||||
|
||||
private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPair: StandardAutoClosingPairConditional): EditOperationResult {
|
||||
private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPairClose: string): EditOperationResult {
|
||||
let commands: ICommand[] = [];
|
||||
for (let i = 0, len = selections.length; i < len; i++) {
|
||||
const selection = selections[i];
|
||||
commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPair.close);
|
||||
commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose);
|
||||
}
|
||||
return new EditOperationResult(EditOperationType.Typing, commands, {
|
||||
shouldPushStackElementBefore: true,
|
||||
@@ -794,9 +817,9 @@ export class TypeOperations {
|
||||
});
|
||||
}
|
||||
|
||||
const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, false);
|
||||
if (autoClosingPairOpenCharType) {
|
||||
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairOpenCharType);
|
||||
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false);
|
||||
if (autoClosingPairClose !== null) {
|
||||
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -838,9 +861,9 @@ export class TypeOperations {
|
||||
}
|
||||
|
||||
if (!isDoingComposition) {
|
||||
const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true);
|
||||
if (autoClosingPairOpenCharType) {
|
||||
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType);
|
||||
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true);
|
||||
if (autoClosingPairClose) {
|
||||
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ export class WordOperations {
|
||||
return selection;
|
||||
}
|
||||
|
||||
if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpen, ctx.model, [ctx.selection])) {
|
||||
if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection])) {
|
||||
const position = ctx.selection.getPosition();
|
||||
return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1);
|
||||
}
|
||||
@@ -438,6 +438,122 @@ export class WordOperations {
|
||||
return new Range(lineNumber, column, position.lineNumber, position.column);
|
||||
}
|
||||
|
||||
public static deleteInsideWord(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range {
|
||||
if (!selection.isEmpty()) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
const position = new Position(selection.positionLineNumber, selection.positionColumn);
|
||||
|
||||
let r = this._deleteInsideWordWhitespace(model, position);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position);
|
||||
}
|
||||
|
||||
private static _charAtIsWhitespace(str: string, index: number): boolean {
|
||||
const charCode = str.charCodeAt(index);
|
||||
return (charCode === CharCode.Space || charCode === CharCode.Tab);
|
||||
}
|
||||
|
||||
private static _deleteInsideWordWhitespace(model: ICursorSimpleModel, position: Position): Range | null {
|
||||
const lineContent = model.getLineContent(position.lineNumber);
|
||||
const lineContentLength = lineContent.length;
|
||||
|
||||
if (lineContentLength === 0) {
|
||||
// empty line
|
||||
return null;
|
||||
}
|
||||
|
||||
let leftIndex = Math.max(position.column - 2, 0);
|
||||
if (!this._charAtIsWhitespace(lineContent, leftIndex)) {
|
||||
// touches a non-whitespace character to the left
|
||||
return null;
|
||||
}
|
||||
|
||||
let rightIndex = Math.min(position.column - 1, lineContentLength - 1);
|
||||
if (!this._charAtIsWhitespace(lineContent, rightIndex)) {
|
||||
// touches a non-whitespace character to the right
|
||||
return null;
|
||||
}
|
||||
|
||||
// walk over whitespace to the left
|
||||
while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) {
|
||||
leftIndex--;
|
||||
}
|
||||
|
||||
// walk over whitespace to the right
|
||||
while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) {
|
||||
rightIndex++;
|
||||
}
|
||||
|
||||
return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2);
|
||||
}
|
||||
|
||||
private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range {
|
||||
const lineContent = model.getLineContent(position.lineNumber);
|
||||
const lineLength = lineContent.length;
|
||||
if (lineLength === 0) {
|
||||
// empty line
|
||||
if (position.lineNumber > 1) {
|
||||
return new Range(position.lineNumber - 1, model.getLineMaxColumn(position.lineNumber - 1), position.lineNumber, 1);
|
||||
} else {
|
||||
if (position.lineNumber < model.getLineCount()) {
|
||||
return new Range(position.lineNumber, 1, position.lineNumber + 1, 1);
|
||||
} else {
|
||||
// empty model
|
||||
return new Range(position.lineNumber, 1, position.lineNumber, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const touchesWord = (word: IFindWordResult) => {
|
||||
return (word.start + 1 <= position.column && position.column <= word.end + 1);
|
||||
};
|
||||
const createRangeWithPosition = (startColumn: number, endColumn: number) => {
|
||||
startColumn = Math.min(startColumn, position.column);
|
||||
endColumn = Math.max(endColumn, position.column);
|
||||
return new Range(position.lineNumber, startColumn, position.lineNumber, endColumn);
|
||||
};
|
||||
const deleteWordAndAdjacentWhitespace = (word: IFindWordResult) => {
|
||||
let startColumn = word.start + 1;
|
||||
let endColumn = word.end + 1;
|
||||
let expandedToTheRight = false;
|
||||
while (endColumn - 1 < lineLength && this._charAtIsWhitespace(lineContent, endColumn - 1)) {
|
||||
expandedToTheRight = true;
|
||||
endColumn++;
|
||||
}
|
||||
if (!expandedToTheRight) {
|
||||
while (startColumn > 1 && this._charAtIsWhitespace(lineContent, startColumn - 2)) {
|
||||
startColumn--;
|
||||
}
|
||||
}
|
||||
return createRangeWithPosition(startColumn, endColumn);
|
||||
};
|
||||
|
||||
const prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
|
||||
if (prevWordOnLine && touchesWord(prevWordOnLine)) {
|
||||
return deleteWordAndAdjacentWhitespace(prevWordOnLine);
|
||||
}
|
||||
const nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position);
|
||||
if (nextWordOnLine && touchesWord(nextWordOnLine)) {
|
||||
return deleteWordAndAdjacentWhitespace(nextWordOnLine);
|
||||
}
|
||||
if (prevWordOnLine && nextWordOnLine) {
|
||||
return createRangeWithPosition(prevWordOnLine.end + 1, nextWordOnLine.start + 1);
|
||||
}
|
||||
if (prevWordOnLine) {
|
||||
return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1);
|
||||
}
|
||||
if (nextWordOnLine) {
|
||||
return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1);
|
||||
}
|
||||
|
||||
return createRangeWithPosition(1, lineLength + 1);
|
||||
}
|
||||
|
||||
public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range {
|
||||
if (!selection.isEmpty()) {
|
||||
return selection;
|
||||
|
||||
@@ -24,6 +24,7 @@ export namespace EditorContextKeys {
|
||||
export const textInputFocus = new RawContextKey<boolean>('textInputFocus', false);
|
||||
|
||||
export const readOnly = new RawContextKey<boolean>('editorReadonly', false);
|
||||
export const inDiffEditor = new RawContextKey<boolean>('inDiffEditor', false);
|
||||
export const columnSelection = new RawContextKey<boolean>('editorColumnSelection', false);
|
||||
export const writable = readOnly.toNegated();
|
||||
export const hasNonEmptySelection = new RawContextKey<boolean>('editorHasSelection', false);
|
||||
|
||||
@@ -695,6 +695,11 @@ export interface ITextModel {
|
||||
*/
|
||||
getEOL(): string;
|
||||
|
||||
/**
|
||||
* Get the end of line sequence predominantly used in the text buffer.
|
||||
*/
|
||||
getEndOfLineSequence(): EndOfLineSequence;
|
||||
|
||||
/**
|
||||
* Get the minimum legal column for line at `lineNumber`
|
||||
*/
|
||||
@@ -850,7 +855,12 @@ export interface ITextModel {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
hasSemanticTokens(): boolean;
|
||||
hasCompleteSemanticTokens(): boolean;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
hasSomeSemanticTokens(): boolean;
|
||||
|
||||
/**
|
||||
* Flush all tokenization state.
|
||||
@@ -1089,12 +1099,17 @@ export interface ITextModel {
|
||||
detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void;
|
||||
|
||||
/**
|
||||
* Push a stack element onto the undo stack. This acts as an undo/redo point.
|
||||
* The idea is to use `pushEditOperations` to edit the model and then to
|
||||
* `pushStackElement` to create an undo/redo stop point.
|
||||
* Close the current undo-redo element.
|
||||
* This offers a way to create an undo/redo stop point.
|
||||
*/
|
||||
pushStackElement(): void;
|
||||
|
||||
/**
|
||||
* Open the current undo-redo element.
|
||||
* This offers a way to remove the current undo/redo stop point.
|
||||
*/
|
||||
popStackElement(): void;
|
||||
|
||||
/**
|
||||
* Push edit operations, basically editing the model. This is the preferred way
|
||||
* of editing the model. The edit operations will land on the undo stack.
|
||||
@@ -1138,7 +1153,7 @@ export interface ITextModel {
|
||||
_applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void;
|
||||
|
||||
/**
|
||||
* Undo edit operations until the first previous stop point created by `pushStackElement`.
|
||||
* Undo edit operations until the previous undo/redo point.
|
||||
* The inverse edit operations will be pushed on the redo stack.
|
||||
* @internal
|
||||
*/
|
||||
@@ -1151,7 +1166,7 @@ export interface ITextModel {
|
||||
canUndo(): boolean;
|
||||
|
||||
/**
|
||||
* Redo edit operations until the next stop point created by `pushStackElement`.
|
||||
* Redo edit operations until the next undo/redo point.
|
||||
* The inverse edit operations will be pushed on the undo stack.
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -199,6 +199,12 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement {
|
||||
}
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
if (!(this._data instanceof SingleModelEditStackData)) {
|
||||
this._data = SingleModelEditStackData.deserialize(this._data);
|
||||
}
|
||||
}
|
||||
|
||||
public undo(): void {
|
||||
if (URI.isUri(this.model)) {
|
||||
// don't have a model
|
||||
@@ -315,6 +321,10 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
|
||||
this._isOpen = false;
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
// cannot reopen
|
||||
}
|
||||
|
||||
public undo(): void {
|
||||
this._isOpen = false;
|
||||
|
||||
@@ -386,6 +396,13 @@ export class EditStack {
|
||||
}
|
||||
}
|
||||
|
||||
public popStackElement(): void {
|
||||
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
|
||||
if (isEditStackElement(lastElement)) {
|
||||
lastElement.open();
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._undoRedoService.removeElements(this._model.uri);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { splitLines } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
@@ -131,7 +132,7 @@ export class MirrorTextModel {
|
||||
// Nothing to insert
|
||||
return;
|
||||
}
|
||||
let insertLines = insertText.split(/\r\n|\r|\n/);
|
||||
let insertLines = splitLines(insertText);
|
||||
if (insertLines.length === 1) {
|
||||
// Inserting text on one line
|
||||
this._setLineText(position.lineNumber - 1,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTex
|
||||
import { SearchData } from 'vs/editor/common/model/textModelSearch';
|
||||
import { countEOL, StringEOL } from 'vs/editor/common/model/tokensStore';
|
||||
import { TextChange } from 'vs/editor/common/model/textChange';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IValidatedEditOperation {
|
||||
sortIndex: number;
|
||||
@@ -32,26 +32,24 @@ export interface IReverseSingleEditOperation extends IValidEditOperation {
|
||||
sortIndex: number;
|
||||
}
|
||||
|
||||
export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
|
||||
private readonly _pieceTree: PieceTreeBase;
|
||||
export class PieceTreeTextBuffer extends Disposable implements ITextBuffer {
|
||||
private _pieceTree: PieceTreeBase;
|
||||
private readonly _BOM: string;
|
||||
private _mightContainRTL: boolean;
|
||||
private _mightContainUnusualLineTerminators: boolean;
|
||||
private _mightContainNonBasicASCII: boolean;
|
||||
|
||||
private readonly _onDidChangeContent: Emitter<void> = new Emitter<void>();
|
||||
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
|
||||
|
||||
constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, containsUnusualLineTerminators: boolean, isBasicASCII: boolean, eolNormalized: boolean) {
|
||||
super();
|
||||
this._BOM = BOM;
|
||||
this._mightContainNonBasicASCII = !isBasicASCII;
|
||||
this._mightContainRTL = containsRTL;
|
||||
this._mightContainUnusualLineTerminators = containsUnusualLineTerminators;
|
||||
this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized);
|
||||
}
|
||||
dispose(): void {
|
||||
this._onDidChangeContent.dispose();
|
||||
}
|
||||
|
||||
// #region TextBuffer
|
||||
public equals(other: ITextBuffer): boolean {
|
||||
|
||||
@@ -387,6 +387,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._isDisposed = true;
|
||||
super.dispose();
|
||||
this._isDisposing = false;
|
||||
// Manually release reference to previous text buffer to avoid large leaks
|
||||
// in case someone leaks a TextModel reference
|
||||
this._buffer = createTextBuffer('', this._options.defaultEOL);
|
||||
}
|
||||
|
||||
private _assertNotDisposed(): void {
|
||||
@@ -830,6 +833,15 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._buffer.getEOL();
|
||||
}
|
||||
|
||||
public getEndOfLineSequence(): model.EndOfLineSequence {
|
||||
this._assertNotDisposed();
|
||||
return (
|
||||
this._buffer.getEOL() === '\n'
|
||||
? model.EndOfLineSequence.LF
|
||||
: model.EndOfLineSequence.CRLF
|
||||
);
|
||||
}
|
||||
|
||||
public getLineMinColumn(lineNumber: number): number {
|
||||
this._assertNotDisposed();
|
||||
return 1;
|
||||
@@ -1213,6 +1225,10 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._commandManager.pushStackElement();
|
||||
}
|
||||
|
||||
public popStackElement(): void {
|
||||
this._commandManager.popStackElement();
|
||||
}
|
||||
|
||||
public pushEOL(eol: model.EndOfLineSequence): void {
|
||||
const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF);
|
||||
if (currentEOL === eol) {
|
||||
@@ -1868,12 +1884,16 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
});
|
||||
}
|
||||
|
||||
public hasSemanticTokens(): boolean {
|
||||
public hasCompleteSemanticTokens(): boolean {
|
||||
return this._tokens2.isComplete();
|
||||
}
|
||||
|
||||
public hasSomeSemanticTokens(): boolean {
|
||||
return !this._tokens2.isEmpty();
|
||||
}
|
||||
|
||||
public setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[]): void {
|
||||
if (this.hasSemanticTokens()) {
|
||||
if (this.hasCompleteSemanticTokens()) {
|
||||
return;
|
||||
}
|
||||
const changedRange = this._tokens2.setPartial(range, tokens);
|
||||
|
||||
@@ -884,6 +884,10 @@ export class TokensStore2 {
|
||||
this._isComplete = false;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return (this._pieces.length === 0);
|
||||
}
|
||||
|
||||
public set(pieces: MultilineTokens2[] | null, isComplete: boolean): void {
|
||||
this._pieces = pieces || [];
|
||||
this._isComplete = isComplete;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationReg
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { iconRegistry, Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
/**
|
||||
* Open ended enum at runtime
|
||||
* @internal
|
||||
@@ -819,17 +819,32 @@ export interface DocumentHighlightProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* The rename provider interface defines the contract between extensions and
|
||||
* the live-rename feature.
|
||||
* The linked editing range provider interface defines the contract between extensions and
|
||||
* the linked editing feature.
|
||||
*/
|
||||
export interface OnTypeRenameProvider {
|
||||
|
||||
wordPattern?: RegExp;
|
||||
export interface LinkedEditingRangeProvider {
|
||||
|
||||
/**
|
||||
* Provide a list of ranges that can be live-renamed together.
|
||||
* Provide a list of ranges that can be edited together.
|
||||
*/
|
||||
provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>;
|
||||
provideLinkedEditingRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<LinkedEditingRanges>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a list of ranges that can be edited together along with a word pattern to describe valid contents.
|
||||
*/
|
||||
export interface LinkedEditingRanges {
|
||||
/**
|
||||
* A list of ranges that can be edited together. The ranges must have
|
||||
* identical length and text content. The ranges cannot overlap
|
||||
*/
|
||||
ranges: IRange[];
|
||||
|
||||
/**
|
||||
* An optional word pattern that describes valid contents for the given ranges.
|
||||
* If no pattern is provided, the language configuration's word pattern will be used.
|
||||
*/
|
||||
wordPattern?: RegExp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1359,7 +1374,10 @@ export interface WorkspaceEditMetadata {
|
||||
needsConfirmation: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
iconPath?: { id: string } | URI | { light: URI, dark: URI };
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
iconPath?: ThemeIcon | URI | { light: URI, dark: URI };
|
||||
}
|
||||
|
||||
export interface WorkspaceFileEditOptions {
|
||||
@@ -1367,6 +1385,10 @@ export interface WorkspaceFileEditOptions {
|
||||
ignoreIfNotExists?: boolean;
|
||||
ignoreIfExists?: boolean;
|
||||
recursive?: boolean;
|
||||
copy?: boolean;
|
||||
folder?: boolean;
|
||||
skipTrashBin?: boolean;
|
||||
maxSize?: number;
|
||||
}
|
||||
|
||||
export interface WorkspaceFileEdit {
|
||||
@@ -1715,7 +1737,7 @@ export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry<Doc
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const OnTypeRenameProviderRegistry = new LanguageFeatureRegistry<OnTypeRenameProvider>();
|
||||
export const LinkedEditingRangeProviderRegistry = new LanguageFeatureRegistry<LinkedEditingRangeProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
||||
@@ -294,17 +294,32 @@ export class StandardAutoClosingPairConditional {
|
||||
* @internal
|
||||
*/
|
||||
export class AutoClosingPairs {
|
||||
// it is useful to be able to get pairs using either end of open and close
|
||||
|
||||
public readonly autoClosingPairsOpen: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
public readonly autoClosingPairsClose: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
/** Key is first character of open */
|
||||
public readonly autoClosingPairsOpenByStart: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
/** Key is last character of open */
|
||||
public readonly autoClosingPairsOpenByEnd: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
/** Key is first character of close */
|
||||
public readonly autoClosingPairsCloseByStart: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
/** Key is last character of close */
|
||||
public readonly autoClosingPairsCloseByEnd: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
/** Key is close. Only has pairs that are a single character */
|
||||
public readonly autoClosingPairsCloseSingleChar: Map<string, StandardAutoClosingPairConditional[]>;
|
||||
|
||||
constructor(autoClosingPairs: StandardAutoClosingPairConditional[]) {
|
||||
this.autoClosingPairsOpen = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsClose = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsOpenByStart = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsOpenByEnd = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsCloseByStart = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsCloseByEnd = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
this.autoClosingPairsCloseSingleChar = new Map<string, StandardAutoClosingPairConditional[]>();
|
||||
for (const pair of autoClosingPairs) {
|
||||
appendEntry(this.autoClosingPairsOpen, pair.open.charAt(pair.open.length - 1), pair);
|
||||
if (pair.close.length === 1) {
|
||||
appendEntry(this.autoClosingPairsClose, pair.close, pair);
|
||||
appendEntry(this.autoClosingPairsOpenByStart, pair.open.charAt(0), pair);
|
||||
appendEntry(this.autoClosingPairsOpenByEnd, pair.open.charAt(pair.open.length - 1), pair);
|
||||
appendEntry(this.autoClosingPairsCloseByStart, pair.close.charAt(0), pair);
|
||||
appendEntry(this.autoClosingPairsCloseByEnd, pair.close.charAt(pair.close.length - 1), pair);
|
||||
if (pair.close.length === 1 && pair.open.length === 1) {
|
||||
appendEntry(this.autoClosingPairsCloseSingleChar, pair.close, pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,6 +622,12 @@ export class LanguageConfigurationRegistryImpl {
|
||||
return null;
|
||||
}
|
||||
const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
||||
|
||||
if (scopedLineTokens.firstCharOffset) {
|
||||
// this line has mixed languages and indentation rules will not work
|
||||
return null;
|
||||
}
|
||||
|
||||
const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId);
|
||||
if (!indentRulesSupport) {
|
||||
return null;
|
||||
|
||||
@@ -12,6 +12,14 @@ export const enum IndentConsts {
|
||||
UNINDENT_MASK = 0b00001000,
|
||||
}
|
||||
|
||||
function resetGlobalRegex(reg: RegExp) {
|
||||
if (reg.global) {
|
||||
reg.lastIndex = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export class IndentRulesSupport {
|
||||
|
||||
private readonly _indentationRules: IndentationRule;
|
||||
@@ -22,7 +30,7 @@ export class IndentRulesSupport {
|
||||
|
||||
public shouldIncrease(text: string): boolean {
|
||||
if (this._indentationRules) {
|
||||
if (this._indentationRules.increaseIndentPattern && this._indentationRules.increaseIndentPattern.test(text)) {
|
||||
if (this._indentationRules.increaseIndentPattern && resetGlobalRegex(this._indentationRules.increaseIndentPattern) && this._indentationRules.increaseIndentPattern.test(text)) {
|
||||
return true;
|
||||
}
|
||||
// if (this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) {
|
||||
@@ -33,14 +41,14 @@ export class IndentRulesSupport {
|
||||
}
|
||||
|
||||
public shouldDecrease(text: string): boolean {
|
||||
if (this._indentationRules && this._indentationRules.decreaseIndentPattern && this._indentationRules.decreaseIndentPattern.test(text)) {
|
||||
if (this._indentationRules && this._indentationRules.decreaseIndentPattern && resetGlobalRegex(this._indentationRules.decreaseIndentPattern) && this._indentationRules.decreaseIndentPattern.test(text)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public shouldIndentNextLine(text: string): boolean {
|
||||
if (this._indentationRules && this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) {
|
||||
if (this._indentationRules && this._indentationRules.indentNextLinePattern && resetGlobalRegex(this._indentationRules.indentNextLinePattern) && this._indentationRules.indentNextLinePattern.test(text)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,7 +57,7 @@ export class IndentRulesSupport {
|
||||
|
||||
public shouldIgnore(text: string): boolean {
|
||||
// the text matches `unIndentedLinePattern`
|
||||
if (this._indentationRules && this._indentationRules.unIndentedLinePattern && this._indentationRules.unIndentedLinePattern.test(text)) {
|
||||
if (this._indentationRules && this._indentationRules.unIndentedLinePattern && resetGlobalRegex(this._indentationRules.unIndentedLinePattern) && this._indentationRules.unIndentedLinePattern.test(text)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens
|
||||
|
||||
function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport): string {
|
||||
let result = `<div class="monaco-tokenized-source">`;
|
||||
let lines = text.split(/\r\n|\r|\n/);
|
||||
let lines = strings.splitLines(text);
|
||||
let currentState = tokenizationSupport.getInitialState();
|
||||
for (let i = 0, len = lines.length; i < len; i++) {
|
||||
let line = lines[i];
|
||||
|
||||
@@ -530,36 +530,30 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
private static readonly _suggestionsLimit = 10000;
|
||||
|
||||
public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> {
|
||||
const model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
public async textualSuggest(modelUrls: string[], leadingWord: string | undefined, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> {
|
||||
|
||||
const sw = new StopWatch(true);
|
||||
const words: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
|
||||
const seen = new Set<string>();
|
||||
|
||||
const wordAt = model.getWordAtPosition(position, wordDefRegExp);
|
||||
if (wordAt) {
|
||||
seen.add(model.getValueInRange(wordAt));
|
||||
}
|
||||
|
||||
for (let word of model.words(wordDefRegExp)) {
|
||||
if (seen.has(word)) {
|
||||
outer: for (let url of modelUrls) {
|
||||
const model = this._getModel(url);
|
||||
if (!model) {
|
||||
continue;
|
||||
}
|
||||
seen.add(word);
|
||||
if (!isNaN(Number(word))) {
|
||||
continue;
|
||||
}
|
||||
words.push(word);
|
||||
if (seen.size > EditorSimpleWorker._suggestionsLimit) {
|
||||
break;
|
||||
|
||||
for (let word of model.words(wordDefRegExp)) {
|
||||
if (word === leadingWord || !isNaN(Number(word))) {
|
||||
continue;
|
||||
}
|
||||
seen.add(word);
|
||||
if (seen.size > EditorSimpleWorker._suggestionsLimit) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { words, duration: sw.elapsed() };
|
||||
|
||||
return { words: Array.from(seen), duration: sw.elapsed() };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IntervalTimer } from 'vs/base/common/async';
|
||||
import { IntervalTimer, timeout } from 'vs/base/common/async';
|
||||
import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker';
|
||||
import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IChange } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
@@ -105,7 +105,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
|
||||
const sw = StopWatch.create(true);
|
||||
const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits));
|
||||
result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed()));
|
||||
return result;
|
||||
return Promise.race([result, timeout(1000).then(() => edits)]);
|
||||
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
@@ -148,20 +148,47 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider {
|
||||
}
|
||||
|
||||
async provideCompletionItems(model: ITextModel, position: Position): Promise<modes.CompletionList | undefined> {
|
||||
const { wordBasedSuggestions } = this._configurationService.getValue<{ wordBasedSuggestions?: boolean }>(model.uri, position, 'editor');
|
||||
if (!wordBasedSuggestions) {
|
||||
type WordBasedSuggestionsConfig = {
|
||||
wordBasedSuggestions?: boolean,
|
||||
wordBasedSuggestionsMode?: 'currentDocument' | 'matchingDocuments' | 'allDocuments'
|
||||
};
|
||||
const config = this._configurationService.getValue<WordBasedSuggestionsConfig>(model.uri, position, 'editor');
|
||||
if (!config.wordBasedSuggestions) {
|
||||
return undefined;
|
||||
}
|
||||
if (!canSyncModel(this._modelService, model.uri)) {
|
||||
return undefined; // File too large
|
||||
|
||||
const models: URI[] = [];
|
||||
if (config.wordBasedSuggestionsMode === 'currentDocument') {
|
||||
// only current file and only if not too large
|
||||
if (canSyncModel(this._modelService, model.uri)) {
|
||||
models.push(model.uri);
|
||||
}
|
||||
} else {
|
||||
// either all files or files of same language
|
||||
for (const candidate of this._modelService.getModels()) {
|
||||
if (!canSyncModel(this._modelService, candidate.uri)) {
|
||||
continue;
|
||||
}
|
||||
if (candidate === model) {
|
||||
models.unshift(candidate.uri);
|
||||
|
||||
} else if (config.wordBasedSuggestionsMode === 'allDocuments' || candidate.getLanguageIdentifier().id === model.getLanguageIdentifier().id) {
|
||||
models.push(candidate.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (models.length === 0) {
|
||||
return undefined; // File too large, no other files
|
||||
}
|
||||
|
||||
const wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
|
||||
const word = model.getWordAtPosition(position);
|
||||
const replace = !word ? Range.fromPositions(position) : new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
|
||||
const insert = replace.setEndPosition(position.lineNumber, position.column);
|
||||
|
||||
const client = await this._workerManager.withWorker();
|
||||
const data = await client.textualSuggest(model.uri, position);
|
||||
const data = await client.textualSuggest(models, word?.word, wordDefRegExp);
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -463,17 +490,11 @@ export class EditorWorkerClient extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
public textualSuggest(resource: URI, position: IPosition): Promise<{ words: string[], duration: number } | null> {
|
||||
return this._withSyncedResources([resource]).then(proxy => {
|
||||
let model = this._modelService.getModel(resource);
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
|
||||
let wordDef = wordDefRegExp.source;
|
||||
let wordDefFlags = regExpFlags(wordDefRegExp);
|
||||
return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags);
|
||||
});
|
||||
public async textualSuggest(resources: URI[], leadingWord: string | undefined, wordDefRegExp: RegExp): Promise<{ words: string[], duration: number } | null> {
|
||||
const proxy = await this._withSyncedResources(resources);
|
||||
const wordDef = wordDefRegExp.source;
|
||||
const wordDefFlags = regExpFlags(wordDefRegExp);
|
||||
return proxy.textualSuggest(resources.map(r => r.toString()), leadingWord, wordDef, wordDefFlags);
|
||||
}
|
||||
|
||||
computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> {
|
||||
|
||||
@@ -93,6 +93,6 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ
|
||||
return modeService.getModeIdByFilepathOrFirstLine(resource);
|
||||
}
|
||||
|
||||
export function cssEscape(val: string): string {
|
||||
return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace
|
||||
export function cssEscape(str: string): string {
|
||||
return str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names.
|
||||
}
|
||||
|
||||
@@ -790,6 +790,10 @@ class ModelSemanticColoring extends Disposable {
|
||||
}
|
||||
const provider = this._getSemanticColoringProvider();
|
||||
if (!provider) {
|
||||
if (this._currentDocumentResponse) {
|
||||
// there are semantic tokens set
|
||||
this._model.setSemanticTokens(null, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
@@ -179,113 +179,119 @@ export enum EditorOption {
|
||||
automaticLayout = 9,
|
||||
autoSurround = 10,
|
||||
codeLens = 11,
|
||||
colorDecorators = 12,
|
||||
columnSelection = 13,
|
||||
comments = 14,
|
||||
contextmenu = 15,
|
||||
copyWithSyntaxHighlighting = 16,
|
||||
cursorBlinking = 17,
|
||||
cursorSmoothCaretAnimation = 18,
|
||||
cursorStyle = 19,
|
||||
cursorSurroundingLines = 20,
|
||||
cursorSurroundingLinesStyle = 21,
|
||||
cursorWidth = 22,
|
||||
disableLayerHinting = 23,
|
||||
disableMonospaceOptimizations = 24,
|
||||
dragAndDrop = 25,
|
||||
emptySelectionClipboard = 26,
|
||||
extraEditorClassName = 27,
|
||||
fastScrollSensitivity = 28,
|
||||
find = 29,
|
||||
fixedOverflowWidgets = 30,
|
||||
folding = 31,
|
||||
foldingStrategy = 32,
|
||||
foldingHighlight = 33,
|
||||
unfoldOnClickAfterEndOfLine = 34,
|
||||
fontFamily = 35,
|
||||
fontInfo = 36,
|
||||
fontLigatures = 37,
|
||||
fontSize = 38,
|
||||
fontWeight = 39,
|
||||
formatOnPaste = 40,
|
||||
formatOnType = 41,
|
||||
glyphMargin = 42,
|
||||
gotoLocation = 43,
|
||||
hideCursorInOverviewRuler = 44,
|
||||
highlightActiveIndentGuide = 45,
|
||||
hover = 46,
|
||||
inDiffEditor = 47,
|
||||
letterSpacing = 48,
|
||||
lightbulb = 49,
|
||||
lineDecorationsWidth = 50,
|
||||
lineHeight = 51,
|
||||
lineNumbers = 52,
|
||||
lineNumbersMinChars = 53,
|
||||
links = 54,
|
||||
matchBrackets = 55,
|
||||
minimap = 56,
|
||||
mouseStyle = 57,
|
||||
mouseWheelScrollSensitivity = 58,
|
||||
mouseWheelZoom = 59,
|
||||
multiCursorMergeOverlapping = 60,
|
||||
multiCursorModifier = 61,
|
||||
multiCursorPaste = 62,
|
||||
occurrencesHighlight = 63,
|
||||
overviewRulerBorder = 64,
|
||||
overviewRulerLanes = 65,
|
||||
padding = 66,
|
||||
parameterHints = 67,
|
||||
peekWidgetDefaultFocus = 68,
|
||||
definitionLinkOpensInPeek = 69,
|
||||
quickSuggestions = 70,
|
||||
quickSuggestionsDelay = 71,
|
||||
readOnly = 72,
|
||||
renameOnType = 73,
|
||||
renderControlCharacters = 74,
|
||||
renderIndentGuides = 75,
|
||||
renderFinalNewline = 76,
|
||||
renderLineHighlight = 77,
|
||||
renderLineHighlightOnlyWhenFocus = 78,
|
||||
renderValidationDecorations = 79,
|
||||
renderWhitespace = 80,
|
||||
revealHorizontalRightPadding = 81,
|
||||
roundedSelection = 82,
|
||||
rulers = 83,
|
||||
scrollbar = 84,
|
||||
scrollBeyondLastColumn = 85,
|
||||
scrollBeyondLastLine = 86,
|
||||
scrollPredominantAxis = 87,
|
||||
selectionClipboard = 88,
|
||||
selectionHighlight = 89,
|
||||
selectOnLineNumbers = 90,
|
||||
showFoldingControls = 91,
|
||||
showUnused = 92,
|
||||
snippetSuggestions = 93,
|
||||
smoothScrolling = 94,
|
||||
stopRenderingLineAfter = 95,
|
||||
suggest = 96,
|
||||
suggestFontSize = 97,
|
||||
suggestLineHeight = 98,
|
||||
suggestOnTriggerCharacters = 99,
|
||||
suggestSelection = 100,
|
||||
tabCompletion = 101,
|
||||
tabIndex = 102,
|
||||
unusualLineTerminators = 103,
|
||||
useTabStops = 104,
|
||||
wordSeparators = 105,
|
||||
wordWrap = 106,
|
||||
wordWrapBreakAfterCharacters = 107,
|
||||
wordWrapBreakBeforeCharacters = 108,
|
||||
wordWrapColumn = 109,
|
||||
wordWrapMinified = 110,
|
||||
wrappingIndent = 111,
|
||||
wrappingStrategy = 112,
|
||||
showDeprecated = 113,
|
||||
editorClassName = 114,
|
||||
pixelRatio = 115,
|
||||
tabFocusMode = 116,
|
||||
layoutInfo = 117,
|
||||
wrappingInfo = 118
|
||||
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,
|
||||
editorClassName = 120,
|
||||
pixelRatio = 121,
|
||||
tabFocusMode = 122,
|
||||
layoutInfo = 123,
|
||||
wrappingInfo = 124
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,8 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
|
||||
export const enum ViewEventType {
|
||||
ViewCompositionStart,
|
||||
ViewCompositionEnd,
|
||||
ViewConfigurationChanged,
|
||||
ViewCursorStateChanged,
|
||||
ViewDecorationsChanged,
|
||||
@@ -29,6 +31,16 @@ export const enum ViewEventType {
|
||||
ViewZonesChanged,
|
||||
}
|
||||
|
||||
export class ViewCompositionStartEvent {
|
||||
public readonly type = ViewEventType.ViewCompositionStart;
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class ViewCompositionEndEvent {
|
||||
public readonly type = ViewEventType.ViewCompositionEnd;
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class ViewConfigurationChangedEvent {
|
||||
|
||||
public readonly type = ViewEventType.ViewConfigurationChanged;
|
||||
@@ -285,7 +297,9 @@ export class ViewZonesChangedEvent {
|
||||
}
|
||||
|
||||
export type ViewEvent = (
|
||||
ViewConfigurationChangedEvent
|
||||
ViewCompositionStartEvent
|
||||
| ViewCompositionEndEvent
|
||||
| ViewConfigurationChangedEvent
|
||||
| ViewCursorStateChangedEvent
|
||||
| ViewDecorationsChangedEvent
|
||||
| ViewFlushedEvent
|
||||
|
||||
@@ -42,6 +42,24 @@ export class LineDecoration {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static extractWrapped(arr: LineDecoration[], startOffset: number, endOffset: number): LineDecoration[] {
|
||||
if (arr.length === 0) {
|
||||
return arr;
|
||||
}
|
||||
const startColumn = startOffset + 1;
|
||||
const endColumn = endOffset + 1;
|
||||
const lineLength = endOffset - startOffset;
|
||||
const r = [];
|
||||
let rLength = 0;
|
||||
for (const dec of arr) {
|
||||
if (dec.endColumn <= startColumn || dec.startColumn >= endColumn) {
|
||||
continue;
|
||||
}
|
||||
r[rLength++] = new LineDecoration(Math.max(1, dec.startColumn - startColumn + 1), Math.min(lineLength + 1, dec.endColumn - startColumn + 1), dec.className, dec.type);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): LineDecoration[] {
|
||||
if (lineDecorations.length === 0) {
|
||||
return [];
|
||||
|
||||
@@ -546,6 +546,23 @@ export class LinesLayout {
|
||||
return verticalOffset > totalHeight;
|
||||
}
|
||||
|
||||
public isInTopPadding(verticalOffset: number): boolean {
|
||||
if (this._paddingTop === 0) {
|
||||
return false;
|
||||
}
|
||||
this._checkPendingChanges();
|
||||
return (verticalOffset < this._paddingTop);
|
||||
}
|
||||
|
||||
public isInBottomPadding(verticalOffset: number): boolean {
|
||||
if (this._paddingBottom === 0) {
|
||||
return false;
|
||||
}
|
||||
this._checkPendingChanges();
|
||||
const totalHeight = this.getLinesTotalHeight();
|
||||
return (verticalOffset >= totalHeight - this._paddingBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first line number that is at or after vertical offset `verticalOffset`.
|
||||
* i.e. if getVerticalOffsetForLine(line) is x and getVerticalOffsetForLine(line + 1) is y, then
|
||||
|
||||
@@ -364,6 +364,13 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
public isAfterLines(verticalOffset: number): boolean {
|
||||
return this._linesLayout.isAfterLines(verticalOffset);
|
||||
}
|
||||
public isInTopPadding(verticalOffset: number): boolean {
|
||||
return this._linesLayout.isInTopPadding(verticalOffset);
|
||||
}
|
||||
isInBottomPadding(verticalOffset: number): boolean {
|
||||
return this._linesLayout.isInBottomPadding(verticalOffset);
|
||||
}
|
||||
|
||||
public getLineNumberAtVerticalOffset(verticalOffset: number): number {
|
||||
return this._linesLayout.getLineNumberAtOrAfterVerticalOffset(verticalOffset);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ export class RenderLineInput {
|
||||
this.containsRTL = containsRTL;
|
||||
this.fauxIndentLength = fauxIndentLength;
|
||||
this.lineTokens = lineTokens;
|
||||
this.lineDecorations = lineDecorations;
|
||||
this.lineDecorations = lineDecorations.sort(LineDecoration.compare);
|
||||
this.tabSize = tabSize;
|
||||
this.startVisibleColumn = startVisibleColumn;
|
||||
this.spaceWidth = spaceWidth;
|
||||
|
||||
@@ -7,8 +7,9 @@ import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier';
|
||||
import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { ILineBreaksComputer, LineBreakData } from 'vs/editor/common/viewModel/viewModel';
|
||||
|
||||
const enum CharacterClass {
|
||||
NONE = 0,
|
||||
|
||||
@@ -12,69 +12,11 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDe
|
||||
import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, LineBreakData, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
||||
export class OutputPosition {
|
||||
outputLineIndex: number;
|
||||
outputOffset: number;
|
||||
|
||||
constructor(outputLineIndex: number, outputOffset: number) {
|
||||
this.outputLineIndex = outputLineIndex;
|
||||
this.outputOffset = outputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export class LineBreakData {
|
||||
constructor(
|
||||
public breakOffsets: number[],
|
||||
public breakOffsetsVisibleColumn: number[],
|
||||
public wrappedTextIndentLength: number
|
||||
) { }
|
||||
|
||||
public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number {
|
||||
if (outputLineIndex === 0) {
|
||||
return outputOffset;
|
||||
} else {
|
||||
return breakOffsets[outputLineIndex - 1] + outputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition {
|
||||
let low = 0;
|
||||
let high = breakOffsets.length - 1;
|
||||
let mid = 0;
|
||||
let midStart = 0;
|
||||
|
||||
while (low <= high) {
|
||||
mid = low + ((high - low) / 2) | 0;
|
||||
|
||||
const midStop = breakOffsets[mid];
|
||||
midStart = mid > 0 ? breakOffsets[mid - 1] : 0;
|
||||
|
||||
if (inputOffset < midStart) {
|
||||
high = mid - 1;
|
||||
} else if (inputOffset >= midStop) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new OutputPosition(mid, inputOffset - midStart);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ILineBreaksComputer {
|
||||
/**
|
||||
* Pass in `previousLineBreakData` if the only difference is in breaking columns!!!
|
||||
*/
|
||||
addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void;
|
||||
finalize(): (LineBreakData | null)[];
|
||||
}
|
||||
|
||||
export interface ILineBreaksComputerFactory {
|
||||
createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer;
|
||||
}
|
||||
@@ -174,6 +116,10 @@ export class CoordinatesConverter implements ICoordinatesConverter {
|
||||
public modelPositionIsVisible(modelPosition: Position): boolean {
|
||||
return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column);
|
||||
}
|
||||
|
||||
public getModelLineViewLineCount(modelLineNumber: number): number {
|
||||
return this._lines.getModelLineViewLineCount(modelLineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
const enum IndentGuideRepeatOption {
|
||||
@@ -473,6 +419,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
|
||||
return this.lines[modelLineNumber - 1].isVisible();
|
||||
}
|
||||
|
||||
public getModelLineViewLineCount(modelLineNumber: number): number {
|
||||
if (modelLineNumber < 1 || modelLineNumber > this.lines.length) {
|
||||
// invalid arguments
|
||||
return 1;
|
||||
}
|
||||
return this.lines[modelLineNumber - 1].getViewLineCount();
|
||||
}
|
||||
|
||||
public setTabSize(newTabSize: number): boolean {
|
||||
if (this.tabSize === newTabSize) {
|
||||
return false;
|
||||
@@ -1431,6 +1385,9 @@ export class IdentityCoordinatesConverter implements ICoordinatesConverter {
|
||||
return true;
|
||||
}
|
||||
|
||||
public getModelLineViewLineCount(modelLineNumber: number): number {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export class IdentityLinesCollection implements IViewModelLinesCollection {
|
||||
|
||||
@@ -33,10 +33,15 @@ export class ViewEventHandler extends Disposable {
|
||||
|
||||
// --- begin event handlers
|
||||
|
||||
public onCompositionStart(e: viewEvents.ViewCompositionStartEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
public onCompositionEnd(e: viewEvents.ViewCompositionEndEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
|
||||
return false;
|
||||
}
|
||||
@@ -94,6 +99,18 @@ export class ViewEventHandler extends Disposable {
|
||||
|
||||
switch (e.type) {
|
||||
|
||||
case viewEvents.ViewEventType.ViewCompositionStart:
|
||||
if (this.onCompositionStart(e)) {
|
||||
shouldRender = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case viewEvents.ViewEventType.ViewCompositionEnd:
|
||||
if (this.onCompositionEnd(e)) {
|
||||
shouldRender = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case viewEvents.ViewEventType.ViewConfigurationChanged:
|
||||
if (this.onConfigurationChanged(e)) {
|
||||
shouldRender = true;
|
||||
|
||||
@@ -61,6 +61,8 @@ export interface IViewLayout {
|
||||
getWhitespaces(): IEditorWhitespace[];
|
||||
|
||||
isAfterLines(verticalOffset: number): boolean;
|
||||
isInTopPadding(verticalOffset: number): boolean;
|
||||
isInBottomPadding(verticalOffset: number): boolean;
|
||||
getLineNumberAtVerticalOffset(verticalOffset: number): number;
|
||||
getVerticalOffsetForLineNumber(lineNumber: number): number;
|
||||
getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null;
|
||||
@@ -82,6 +84,65 @@ export interface ICoordinatesConverter {
|
||||
convertModelPositionToViewPosition(modelPosition: Position): Position;
|
||||
convertModelRangeToViewRange(modelRange: Range): Range;
|
||||
modelPositionIsVisible(modelPosition: Position): boolean;
|
||||
getModelLineViewLineCount(modelLineNumber: number): number;
|
||||
}
|
||||
|
||||
export class OutputPosition {
|
||||
outputLineIndex: number;
|
||||
outputOffset: number;
|
||||
|
||||
constructor(outputLineIndex: number, outputOffset: number) {
|
||||
this.outputLineIndex = outputLineIndex;
|
||||
this.outputOffset = outputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export class LineBreakData {
|
||||
constructor(
|
||||
public breakOffsets: number[],
|
||||
public breakOffsetsVisibleColumn: number[],
|
||||
public wrappedTextIndentLength: number
|
||||
) { }
|
||||
|
||||
public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number {
|
||||
if (outputLineIndex === 0) {
|
||||
return outputOffset;
|
||||
} else {
|
||||
return breakOffsets[outputLineIndex - 1] + outputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition {
|
||||
let low = 0;
|
||||
let high = breakOffsets.length - 1;
|
||||
let mid = 0;
|
||||
let midStart = 0;
|
||||
|
||||
while (low <= high) {
|
||||
mid = low + ((high - low) / 2) | 0;
|
||||
|
||||
const midStop = breakOffsets[mid];
|
||||
midStart = mid > 0 ? breakOffsets[mid - 1] : 0;
|
||||
|
||||
if (inputOffset < midStart) {
|
||||
high = mid - 1;
|
||||
} else if (inputOffset >= midStop) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new OutputPosition(mid, inputOffset - midStart);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ILineBreaksComputer {
|
||||
/**
|
||||
* Pass in `previousLineBreakData` if the only difference is in breaking columns!!!
|
||||
*/
|
||||
addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void;
|
||||
finalize(): (LineBreakData | null)[];
|
||||
}
|
||||
|
||||
export interface IViewModel extends ICursorSimpleModel {
|
||||
@@ -103,6 +164,8 @@ export interface IViewModel extends ICursorSimpleModel {
|
||||
setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void;
|
||||
tokenizeViewport(): void;
|
||||
setHasFocus(hasFocus: boolean): void;
|
||||
onCompositionStart(): void;
|
||||
onCompositionEnd(): void;
|
||||
onDidColorThemeChange(): void;
|
||||
|
||||
getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[];
|
||||
@@ -142,6 +205,7 @@ export interface IViewModel extends ICursorSimpleModel {
|
||||
|
||||
//#endregion
|
||||
|
||||
createLineBreaksComputer(): ILineBreaksComputer;
|
||||
|
||||
//#region cursor
|
||||
getPrimaryCursorState(): CursorState;
|
||||
|
||||
@@ -21,7 +21,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout';
|
||||
import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
@@ -153,6 +153,10 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
this._eventDispatcher.dispose();
|
||||
}
|
||||
|
||||
public createLineBreaksComputer(): ILineBreaksComputer {
|
||||
return this._lines.createLineBreaksComputer();
|
||||
}
|
||||
|
||||
public addViewEventHandler(eventHandler: ViewEventHandler): void {
|
||||
this._eventDispatcher.addViewEventHandler(eventHandler);
|
||||
}
|
||||
@@ -179,6 +183,14 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus));
|
||||
}
|
||||
|
||||
public onCompositionStart(): void {
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent());
|
||||
}
|
||||
|
||||
public onCompositionEnd(): void {
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionEndEvent());
|
||||
}
|
||||
|
||||
public onDidColorThemeChange(): void {
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user