Merge from vscode e3c4990c67c40213af168300d1cfeb71d680f877 (#16569)

This commit is contained in:
Cory Rivera
2021-08-25 16:28:29 -07:00
committed by GitHub
parent ab1112bfb3
commit cb7b7da0a4
1752 changed files with 59525 additions and 33878 deletions

View File

@@ -24,6 +24,9 @@ export interface IShiftCommandOpts {
const repeatCache: { [str: string]: string[]; } = Object.create(null);
export function cachedStringRepeat(str: string, count: number): string {
if (count <= 0) {
return '';
}
if (!repeatCache[str]) {
repeatCache[str] = ['', str];
}

View File

@@ -204,6 +204,7 @@ function migrateOptions(options: IEditorOptions): void {
mapping['method'] = 'showMethods';
mapping['function'] = 'showFunctions';
mapping['constructor'] = 'showConstructors';
mapping['deprecated'] = 'showDeprecated';
mapping['field'] = 'showFields';
mapping['variable'] = 'showVariables';
mapping['class'] = 'showClasses';

View File

@@ -382,8 +382,9 @@ export interface IEditorOptions {
* Suggest options.
*/
suggest?: ISuggestOptions;
inlineSuggest?: IInlineSuggestOptions;
/**
* Smart select opptions;
* Smart select options.
*/
smartSelect?: ISmartSelectOptions;
/**
@@ -637,7 +638,11 @@ export interface IEditorOptions {
/**
* Control the behavior and rendering of the inline hints.
*/
inlineHints?: IEditorInlineHintsOptions;
inlayHints?: IEditorInlayHintsOptions;
/**
* Control if the editor should use shadow DOM.
*/
useShadowDOM?: boolean;
}
/**
@@ -2401,12 +2406,12 @@ class EditorLightbulb extends BaseEditorOption<EditorOption.lightbulb, EditorLig
//#endregion
//#region inlineHints
//#region inlayHints
/**
* Configuration options for editor inlineHints
* Configuration options for editor inlayHints
*/
export interface IEditorInlineHintsOptions {
export interface IEditorInlayHintsOptions {
/**
* Enable the inline hints.
* Defaults to true.
@@ -2426,39 +2431,39 @@ export interface IEditorInlineHintsOptions {
fontFamily?: string;
}
export type EditorInlineHintsOptions = Readonly<Required<IEditorInlineHintsOptions>>;
export type EditorInlayHintsOptions = Readonly<Required<IEditorInlayHintsOptions>>;
class EditorInlineHints extends BaseEditorOption<EditorOption.inlineHints, EditorInlineHintsOptions> {
class EditorInlayHints extends BaseEditorOption<EditorOption.inlayHints, EditorInlayHintsOptions> {
constructor() {
const defaults: EditorInlineHintsOptions = { enabled: true, fontSize: 0, fontFamily: EDITOR_FONT_DEFAULTS.fontFamily };
const defaults: EditorInlayHintsOptions = { enabled: true, fontSize: 0, fontFamily: EDITOR_FONT_DEFAULTS.fontFamily };
super(
EditorOption.inlineHints, 'inlineHints', defaults,
EditorOption.inlayHints, 'inlayHints', defaults,
{
'editor.inlineHints.enabled': {
'editor.inlayHints.enabled': {
type: 'boolean',
default: defaults.enabled,
description: nls.localize('inlineHints.enable', "Enables the inline hints in the editor.")
description: nls.localize('inlayHints.enable', "Enables the inlay hints in the editor.")
},
'editor.inlineHints.fontSize': {
'editor.inlayHints.fontSize': {
type: 'number',
default: defaults.fontSize,
description: nls.localize('inlineHints.fontSize', "Controls font size of inline hints in the editor. When set to `0`, the 90% of `#editor.fontSize#` is used.")
description: nls.localize('inlayHints.fontSize', "Controls font size of inlay hints in the editor. When set to `0`, the 90% of `#editor.fontSize#` is used.")
},
'editor.inlineHints.fontFamily': {
'editor.inlayHints.fontFamily': {
type: 'string',
default: defaults.fontFamily,
description: nls.localize('inlineHints.fontFamily', "Controls font family of inline hints in the editor.")
description: nls.localize('inlayHints.fontFamily', "Controls font family of inlay hints in the editor.")
},
}
);
}
public validate(_input: any): EditorInlineHintsOptions {
public validate(_input: any): EditorInlayHintsOptions {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorInlineHintsOptions;
const input = _input as IEditorInlayHintsOptions;
return {
enabled: boolean(input.enabled, this.defaultValue.enabled),
fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100),
@@ -3141,6 +3146,51 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
//#endregion
//#region inlineSuggest
export interface IInlineSuggestOptions {
/**
* Enable or disable the rendering of automatic inline completions.
*/
enabled?: boolean;
}
export type InternalInlineSuggestOptions = Readonly<Required<IInlineSuggestOptions>>;
/**
* Configuration options for inline suggestions
*/
class InlineEditorSuggest extends BaseEditorOption<EditorOption.inlineSuggest, InternalInlineSuggestOptions> {
constructor() {
const defaults: InternalInlineSuggestOptions = {
enabled: false
};
super(
EditorOption.inlineSuggest, 'inlineSuggest', defaults,
{
'editor.inlineSuggest.enabled': {
type: 'boolean',
default: defaults.enabled,
description: nls.localize('inlineSuggest.enabled', "Controls whether to automatically show inline suggestions in the editor.")
},
}
);
}
public validate(_input: any): InternalInlineSuggestOptions {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IInlineSuggestOptions;
return {
enabled: boolean(input.enabled, this.defaultValue.enabled),
};
}
}
//#endregion
//#region suggest
/**
@@ -3175,6 +3225,10 @@ export interface ISuggestOptions {
* Enable or disable the suggest status bar.
*/
showStatusBar?: boolean;
/**
* Enable or disable the rendering of the suggestion preview.
*/
preview?: boolean;
/**
* Show details inline with the label. Defaults to true.
*/
@@ -3191,6 +3245,10 @@ export interface ISuggestOptions {
* Show constructor-suggestions.
*/
showConstructors?: boolean;
/**
* Show deprecated-suggestions.
*/
showDeprecated?: boolean;
/**
* Show field-suggestions.
*/
@@ -3302,10 +3360,12 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
shareSuggestSelections: false,
showIcons: true,
showStatusBar: false,
preview: false,
showInlineDetails: true,
showMethods: true,
showFunctions: true,
showConstructors: true,
showDeprecated: true,
showFields: true,
showVariables: true,
showClasses: true,
@@ -3374,7 +3434,11 @@ 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.preview': {
type: 'boolean',
default: defaults.preview,
description: nls.localize('suggest.preview', "Controls whether to preview the suggestion outcome in the editor.")
},
'editor.suggest.showInlineDetails': {
type: 'boolean',
default: defaults.showInlineDetails,
@@ -3403,6 +3467,11 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
default: true,
markdownDescription: nls.localize('editor.suggest.showConstructors', "When enabled IntelliSense shows `constructor`-suggestions.")
},
'editor.suggest.showDeprecated': {
type: 'boolean',
default: true,
markdownDescription: nls.localize('editor.suggest.showDeprecated', "When enabled IntelliSense shows `deprecated`-suggestions.")
},
'editor.suggest.showFields': {
type: 'boolean',
default: true,
@@ -3545,10 +3614,12 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
shareSuggestSelections: boolean(input.shareSuggestSelections, this.defaultValue.shareSuggestSelections),
showIcons: boolean(input.showIcons, this.defaultValue.showIcons),
showStatusBar: boolean(input.showStatusBar, this.defaultValue.showStatusBar),
preview: boolean(input.preview, this.defaultValue.preview),
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),
showDeprecated: boolean(input.showDeprecated, this.defaultValue.showDeprecated),
showFields: boolean(input.showFields, this.defaultValue.showFields),
showVariables: boolean(input.showVariables, this.defaultValue.showVariables),
showClasses: boolean(input.showClasses, this.defaultValue.showClasses),
@@ -3793,6 +3864,7 @@ export const enum EditorOption {
highlightActiveIndentGuide,
hover,
inDiffEditor,
inlineSuggest,
letterSpacing,
lightbulb,
lineDecorationsWidth,
@@ -3852,6 +3924,7 @@ export const enum EditorOption {
tabCompletion,
tabIndex,
unusualLineTerminators,
useShadowDOM,
useTabStops,
wordSeparators,
wordWrap,
@@ -3863,7 +3936,7 @@ export const enum EditorOption {
wrappingIndent,
wrappingStrategy,
showDeprecated,
inlineHints,
inlayHints,
// Leave these at the end (because they have dependencies!)
editorClassName,
pixelRatio,
@@ -4385,7 +4458,7 @@ export const EditorOptions = {
EditorOption.showDeprecated, 'showDeprecated', true,
{ description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") }
)),
inlineHints: register(new EditorInlineHints()),
inlayHints: register(new EditorInlayHints()),
snippetSuggestions: register(new EditorStringEnumOption(
EditorOption.snippetSuggestions, 'snippetSuggestions',
'inline' as 'top' | 'bottom' | 'inline' | 'none',
@@ -4410,6 +4483,7 @@ export const EditorOptions = {
10000, -1, Constants.MAX_SAFE_SMALL_INTEGER,
)),
suggest: register(new EditorSuggest()),
inlineSuggest: register(new InlineEditorSuggest()),
suggestFontSize: register(new EditorIntOption(
EditorOption.suggestFontSize, 'suggestFontSize',
0, 0, 1000,
@@ -4467,6 +4541,9 @@ export const EditorOptions = {
description: nls.localize('unusualLineTerminators', "Remove unusual line terminators that might cause problems.")
}
)),
useShadowDOM: register(new EditorBooleanOption(
EditorOption.useShadowDOM, 'useShadowDOM', true
)),
useTabStops: register(new EditorBooleanOption(
EditorOption.useTabStops, 'useTabStops', true,
{ description: nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") }

View File

@@ -415,6 +415,7 @@ export class Cursor extends Disposable {
autoClosedCharactersDeltaDecorations.push({
range: autoClosedCharactersRanges[i],
options: {
description: 'auto-closed-character',
inlineClassName: 'auto-closed-character',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
@@ -422,6 +423,7 @@ export class Cursor extends Disposable {
autoClosedEnclosingDeltaDecorations.push({
range: autoClosedEnclosingRanges[i],
options: {
description: 'auto-closed-enclosing',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
});

View File

@@ -11,7 +11,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon';
import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model';
import { ITextModel, PositionNormalizationAffinity, TextModelResolvedOptions } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { AutoClosingPairs, IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration';
@@ -39,9 +39,11 @@ export const enum RevealTarget {
*/
export const enum EditOperationType {
Other = 0,
Typing = 1,
DeletingLeft = 2,
DeletingRight = 3
DeletingRight = 3,
TypingOther = 4,
TypingFirstSpace = 5,
TypingConsecutiveSpace = 6,
}
export interface CharacterMap {
@@ -219,6 +221,12 @@ export interface ICursorSimpleModel {
getLineMaxColumn(lineNumber: number): number;
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
/**
@@ -450,6 +458,55 @@ export class CursorColumns {
return result;
}
/**
* Returns an array that maps one based columns to one based visible columns. The entry at position 0 is -1.
*/
public static visibleColumnsByColumns(lineContent: string, tabSize: number): number[] {
const endOffset = lineContent.length;
let result = new Array<number>();
result.push(-1);
let pos = 0;
let i = 0;
while (i < endOffset) {
const codePoint = strings.getNextCodePoint(lineContent, endOffset, i);
i += (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
result.push(pos);
if (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN) {
result.push(pos);
}
if (codePoint === CharCode.Tab) {
pos = CursorColumns.nextRenderTabStop(pos, tabSize);
} else {
let graphemeBreakType = strings.getGraphemeBreakType(codePoint);
while (i < endOffset) {
const nextCodePoint = strings.getNextCodePoint(lineContent, endOffset, i);
const nextGraphemeBreakType = strings.getGraphemeBreakType(nextCodePoint);
if (strings.breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) {
break;
}
i += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
result.push(pos);
if (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN) {
result.push(pos);
}
graphemeBreakType = nextGraphemeBreakType;
}
if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) {
pos = pos + 2;
} else {
pos = pos + 1;
}
}
}
result.push(pos);
return result;
}
public static toStatusbarColumn(lineContent: string, column: number, tabSize: number): number {
const lineContentLength = lineContent.length;
const endOffset = column - 1 < lineContentLength ? column - 1 : lineContentLength;
@@ -562,14 +619,14 @@ export class CursorColumns {
* 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;
return Math.max(0, column - 1 - (column - 1) % tabSize);
}
/**
* 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;
return Math.max(0, column - 1 - (column - 1) % indentSize);
}
}

View File

@@ -12,6 +12,7 @@ import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand } from 'vs/editor/common/editorCommon';
import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
import { Position } from 'vs/editor/common/core/position';
export class DeleteOperations {
@@ -25,7 +26,7 @@ export class DeleteOperations {
if (deleteSelection.isEmpty()) {
let position = selection.getPosition();
let rightOfPosition = MoveOperations.right(config, model, position.lineNumber, position.column);
let rightOfPosition = MoveOperations.right(config, model, position);
deleteSelection = new Range(
rightOfPosition.lineNumber,
rightOfPosition.column,
@@ -141,63 +142,72 @@ export class DeleteOperations {
}
public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], autoClosedCharacters: Range[]): [boolean, Array<ICommand | null>] {
if (this.isAutoClosingPairDelete(config.autoClosingDelete, config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections, autoClosedCharacters)) {
return this._runAutoClosingPairDelete(config, model, selections);
}
let commands: Array<ICommand | null> = [];
const commands: Array<ICommand | null> = [];
let shouldPushStackElementBefore = (prevEditOperationType !== EditOperationType.DeletingLeft);
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
let deleteRange = DeleteOperations.getDeleteRange(selections[i], model, config);
let deleteSelection: Range = selection;
if (deleteSelection.isEmpty()) {
let position = selection.getPosition();
if (config.useTabStops && position.column > 1) {
let lineContent = model.getLineContent(position.lineNumber);
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
let lastIndentationColumn = (
firstNonWhitespaceIndex === -1
? /* entire string is whitespace */lineContent.length + 1
: firstNonWhitespaceIndex + 1
);
if (position.column <= lastIndentationColumn) {
let fromVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, position);
let toVisibleColumn = CursorColumns.prevIndentTabStop(fromVisibleColumn, config.indentSize);
let toColumn = CursorColumns.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn);
deleteSelection = new Range(position.lineNumber, toColumn, position.lineNumber, position.column);
} else {
deleteSelection = new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column);
}
} else {
let leftOfPosition = MoveOperations.left(config, model, position.lineNumber, position.column);
deleteSelection = new Range(
leftOfPosition.lineNumber,
leftOfPosition.column,
position.lineNumber,
position.column
);
}
}
if (deleteSelection.isEmpty()) {
// Probably at beginning of file => ignore
// Ignore empty delete ranges, as they have no effect
// They happen if the cursor is at the beginning of the file.
if (deleteRange.isEmpty()) {
commands[i] = null;
continue;
}
if (deleteSelection.startLineNumber !== deleteSelection.endLineNumber) {
if (deleteRange.startLineNumber !== deleteRange.endLineNumber) {
shouldPushStackElementBefore = true;
}
commands[i] = new ReplaceCommand(deleteSelection, '');
commands[i] = new ReplaceCommand(deleteRange, '');
}
return [shouldPushStackElementBefore, commands];
}
private static getDeleteRange(selection: Selection, model: ICursorSimpleModel, config: CursorConfiguration,): Range {
if (!selection.isEmpty()) {
return selection;
}
const position = selection.getPosition();
// Unintend when using tab stops and cursor is within indentation
if (config.useTabStops && position.column > 1) {
const lineContent = model.getLineContent(position.lineNumber);
const firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
const lastIndentationColumn = (
firstNonWhitespaceIndex === -1
? /* entire string is whitespace */ lineContent.length + 1
: firstNonWhitespaceIndex + 1
);
if (position.column <= lastIndentationColumn) {
const fromVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, position);
const toVisibleColumn = CursorColumns.prevIndentTabStop(fromVisibleColumn, config.indentSize);
const toColumn = CursorColumns.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn);
return new Range(position.lineNumber, toColumn, position.lineNumber, position.column);
}
}
return Range.fromPositions(DeleteOperations.getPositionAfterDeleteLeft(position, model), position);
}
private static getPositionAfterDeleteLeft(position: Position, model: ICursorSimpleModel): Position {
if (position.column > 1) {
// Convert 1-based columns to 0-based offsets and back.
const idx = strings.getLeftDeleteOffset(position.column - 1, model.getLineContent(position.lineNumber));
return position.with(undefined, idx + 1);
} else if (position.lineNumber > 1) {
const newLine = position.lineNumber - 1;
return new Position(newLine, model.getLineMaxColumn(newLine));
} else {
return position;
}
}
public static cut(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): EditOperationResult {

View File

@@ -419,29 +419,11 @@ export class CursorMoveCommands {
}
private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
const hasMultipleCursors = (cursors.length > 1);
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
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) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
result[i] = CursorState.fromViewState(newViewState);
}
return result;
return cursors.map(cursor =>
CursorState.fromViewState(
MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
)
);
}
private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
@@ -456,29 +438,11 @@ export class CursorMoveCommands {
}
private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
const hasMultipleCursors = (cursors.length > 1);
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
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) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
result[i] = CursorState.fromViewState(newViewState);
}
return result;
return cursors.map(cursor =>
CursorState.fromViewState(
MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
)
);
}
private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {

View File

@@ -9,6 +9,7 @@ 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';
import { PositionNormalizationAffinity } from 'vs/editor/common/model';
export class CursorPosition {
_cursorPositionBrand: void;
@@ -25,51 +26,86 @@ export class CursorPosition {
}
export class MoveOperations {
public static leftPosition(model: ICursorSimpleModel, lineNumber: number, column: number): Position {
if (column > model.getLineMinColumn(lineNumber)) {
column = column - strings.prevCharLength(model.getLineContent(lineNumber), column - 1);
} else if (lineNumber > 1) {
lineNumber = lineNumber - 1;
column = model.getLineMaxColumn(lineNumber);
public static leftPosition(model: ICursorSimpleModel, position: Position): Position {
if (position.column > model.getLineMinColumn(position.lineNumber)) {
return position.delta(undefined, -strings.prevCharLength(model.getLineContent(position.lineNumber), position.column - 1));
} else if (position.lineNumber > 1) {
const newLineNumber = position.lineNumber - 1;
return new Position(newLineNumber, model.getLineMaxColumn(newLineNumber));
} else {
return position;
}
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 - 1, tabSize, Direction.Left);
if (newPosition === -1 || newPosition + 1 < minColumn) {
return this.leftPosition(model, lineNumber, column);
private static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, position: Position, tabSize: number): Position {
if (position.column <= model.getLineIndentColumn(position.lineNumber)) {
const minColumn = model.getLineMinColumn(position.lineNumber);
const lineContent = model.getLineContent(position.lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - 1, tabSize, Direction.Left);
if (newPosition !== -1 && newPosition + 1 >= minColumn) {
return new Position(position.lineNumber, newPosition + 1);
}
}
return new Position(lineNumber, newPosition + 1);
return this.leftPosition(model, position);
}
public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
private static left(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): CursorPosition {
const pos = config.stickyTabStops
? MoveOperations.leftPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize)
: MoveOperations.leftPosition(model, lineNumber, column);
? MoveOperations.leftPositionAtomicSoftTabs(model, position, config.tabSize)
: MoveOperations.leftPosition(model, position);
return new CursorPosition(pos.lineNumber, pos.column, 0);
}
/**
* @param noOfColumns Must be either `1`
* or `Math.round(viewModel.getLineContent(viewLineNumber).length / 2)` (for half lines).
*/
public static moveLeft(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, noOfColumns: number): SingleCursorState {
let lineNumber: number,
column: number;
if (cursor.hasSelection() && !inSelectionMode) {
// If we are in selection mode, move left without selection cancels selection and puts cursor at the beginning of the selection
// If the user has a selection and does not want to extend it,
// put the cursor at the beginning of the selection.
lineNumber = cursor.selection.startLineNumber;
column = cursor.selection.startColumn;
} else {
let r = MoveOperations.left(config, model, cursor.position.lineNumber, cursor.position.column - (noOfColumns - 1));
lineNumber = r.lineNumber;
column = r.column;
// This has no effect if noOfColumns === 1.
// It is ok to do so in the half-line scenario.
const pos = cursor.position.delta(undefined, -(noOfColumns - 1));
// We clip the position before normalization, as normalization is not defined
// for possibly negative columns.
const normalizedPos = model.normalizePosition(MoveOperations.clipPositionColumn(pos, model), PositionNormalizationAffinity.Left);
const p = MoveOperations.left(config, model, normalizedPos);
lineNumber = p.lineNumber;
column = p.column;
}
return cursor.move(inSelectionMode, lineNumber, column, 0);
}
/**
* Adjusts the column so that it is within min/max of the line.
*/
private static clipPositionColumn(position: Position, model: ICursorSimpleModel): Position {
return new Position(
position.lineNumber,
MoveOperations.clipRange(position.column, model.getLineMinColumn(position.lineNumber),
model.getLineMaxColumn(position.lineNumber))
);
}
private static clipRange(value: number, min: number, max: number): number {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
public static rightPosition(model: ICursorSimpleModel, lineNumber: number, column: number): Position {
if (column < model.getLineMaxColumn(lineNumber)) {
column = column + strings.nextCharLength(model.getLineContent(lineNumber), column - 1);
@@ -81,18 +117,20 @@ export class MoveOperations {
}
public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position {
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Right);
if (newPosition === -1) {
return this.rightPosition(model, lineNumber, column);
if (column < model.getLineIndentColumn(lineNumber)) {
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Right);
if (newPosition !== -1) {
return new Position(lineNumber, newPosition + 1);
}
}
return new Position(lineNumber, newPosition + 1);
return this.rightPosition(model, lineNumber, column);
}
public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
public static right(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): CursorPosition {
const pos = config.stickyTabStops
? MoveOperations.rightPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize, config.indentSize)
: MoveOperations.rightPosition(model, lineNumber, column);
? MoveOperations.rightPositionAtomicSoftTabs(model, position.lineNumber, position.column, config.tabSize, config.indentSize)
: MoveOperations.rightPosition(model, position.lineNumber, position.column);
return new CursorPosition(pos.lineNumber, pos.column, 0);
}
@@ -105,7 +143,9 @@ export class MoveOperations {
lineNumber = cursor.selection.endLineNumber;
column = cursor.selection.endColumn;
} else {
let r = MoveOperations.right(config, model, cursor.position.lineNumber, cursor.position.column + (noOfColumns - 1));
const pos = cursor.position.delta(undefined, noOfColumns - 1);
const normalizedPos = model.normalizePosition(MoveOperations.clipPositionColumn(pos, model), PositionNormalizationAffinity.Right);
const r = MoveOperations.right(config, model, normalizedPos);
lineNumber = r.lineNumber;
column = r.column;
}

View File

@@ -260,8 +260,8 @@ export class TypeOperations {
public static compositionType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): EditOperationResult {
const commands = selections.map(selection => this._compositionType(model, selection, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta));
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing),
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: shouldPushStackElementBetween(prevEditOperationType, EditOperationType.TypingOther),
shouldPushStackElementAfter: false
});
}
@@ -484,8 +484,8 @@ export class TypeOperations {
const typeSelection = new Range(position.lineNumber, position.column, position.lineNumber, position.column + 1);
commands[i] = new ReplaceCommand(typeSelection, ch);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing),
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: shouldPushStackElementBetween(prevEditOperationType, EditOperationType.TypingOther),
shouldPushStackElementAfter: false
});
}
@@ -636,7 +636,7 @@ export class TypeOperations {
const selection = selections[i];
commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: true,
shouldPushStackElementAfter: false
});
@@ -762,7 +762,7 @@ export class TypeOperations {
let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column);
const command = new ReplaceCommand(typeSelection, typeText);
return new EditOperationResult(EditOperationType.Typing, [command], {
return new EditOperationResult(getTypingOperation(typeText, prevEditOperationType), [command], {
shouldPushStackElementBefore: false,
shouldPushStackElementAfter: true
});
@@ -803,7 +803,7 @@ export class TypeOperations {
if (this._isAutoClosingOvertype(config, model, selections, autoClosedCharacters, ch)) {
// Unfortunately, the close character is at this point "doubled", so we need to delete it...
const commands = selections.map(s => new ReplaceCommand(new Range(s.positionLineNumber, s.positionColumn, s.positionLineNumber, s.positionColumn + 1), '', false));
return new EditOperationResult(EditOperationType.Typing, commands, {
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: true,
shouldPushStackElementAfter: false
});
@@ -824,7 +824,7 @@ export class TypeOperations {
for (let i = 0, len = selections.length; i < len; i++) {
commands[i] = TypeOperations._enter(config, model, false, selections[i]);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: true,
shouldPushStackElementAfter: false,
});
@@ -841,7 +841,7 @@ export class TypeOperations {
}
}
if (!autoIndentFails) {
return new EditOperationResult(EditOperationType.Typing, commands, {
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: true,
shouldPushStackElementAfter: false,
});
@@ -877,12 +877,10 @@ export class TypeOperations {
for (let i = 0, len = selections.length; i < len; i++) {
commands[i] = new ReplaceCommand(selections[i], ch);
}
let shouldPushStackElementBefore = (prevEditOperationType !== EditOperationType.Typing);
if (ch === ' ') {
shouldPushStackElementBefore = true;
}
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: shouldPushStackElementBefore,
const opType = getTypingOperation(ch, prevEditOperationType);
return new EditOperationResult(opType, commands, {
shouldPushStackElementBefore: shouldPushStackElementBetween(prevEditOperationType, opType),
shouldPushStackElementAfter: false
});
}
@@ -892,8 +890,9 @@ export class TypeOperations {
for (let i = 0, len = selections.length; i < len; i++) {
commands[i] = new ReplaceCommand(selections[i], str);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing),
const opType = getTypingOperation(str, prevEditOperationType);
return new EditOperationResult(opType, commands, {
shouldPushStackElementBefore: shouldPushStackElementBetween(prevEditOperationType, opType),
shouldPushStackElementAfter: false
});
}
@@ -965,3 +964,40 @@ export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorSt
return super.computeCursorState(model, helper);
}
}
function getTypingOperation(typedText: string, previousTypingOperation: EditOperationType): EditOperationType {
if (typedText === ' ') {
return previousTypingOperation === EditOperationType.TypingFirstSpace
|| previousTypingOperation === EditOperationType.TypingConsecutiveSpace
? EditOperationType.TypingConsecutiveSpace
: EditOperationType.TypingFirstSpace;
}
return EditOperationType.TypingOther;
}
function shouldPushStackElementBetween(previousTypingOperation: EditOperationType, typingOperation: EditOperationType): boolean {
if (isTypingOperation(previousTypingOperation) && !isTypingOperation(typingOperation)) {
// Always set an undo stop before non-type operations
return true;
}
if (previousTypingOperation === EditOperationType.TypingFirstSpace) {
// `abc |d`: No undo stop
// `abc |d`: Undo stop
return false;
}
// Insert undo stop between different operation types
return normalizeOperationType(previousTypingOperation) !== normalizeOperationType(typingOperation);
}
function normalizeOperationType(type: EditOperationType): EditOperationType | 'space' {
return (type === EditOperationType.TypingConsecutiveSpace || type === EditOperationType.TypingFirstSpace)
? 'space'
: type;
}
function isTypingOperation(type: EditOperationType): boolean {
return type === EditOperationType.TypingOther
|| type === EditOperationType.TypingFirstSpace
|| type === EditOperationType.TypingConsecutiveSpace;
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ColorId, LanguageId, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
export interface IViewLineTokens {
equals(other: IViewLineTokens): boolean;
@@ -22,6 +22,20 @@ export class LineTokens implements IViewLineTokens {
private readonly _tokensCount: number;
private readonly _text: string;
public static createEmpty(lineContent: string): LineTokens {
const defaultMetadata = (
(FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
) >>> 0;
const tokens = new Uint32Array(2);
tokens[0] = lineContent.length;
tokens[1] = defaultMetadata;
return new LineTokens(tokens, lineContent);
}
constructor(tokens: Uint32Array, text: string) {
this._tokens = tokens;
this._tokensCount = (this._tokens.length >>> 1);

View File

@@ -45,6 +45,10 @@ class LineSequence implements ISequence {
return elements;
}
public getStrictElement(index: number): string {
return this.lines[index];
}
public getStartLineNumber(i: number): number {
return i + 1;
}

View File

@@ -503,7 +503,7 @@ export interface IEditor {
* Change the decorations. All decorations added through this changeAccessor
* will get the ownerId of the editor (meaning they will not show up in other
* editors).
* @see `ITextModel.changeDecorations`
* @see {@link ITextModel.changeDecorations}
* @internal
*/
changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any;
@@ -639,6 +639,7 @@ export interface IContentDecorationRenderOptions {
textDecoration?: string;
color?: string | ThemeColor;
backgroundColor?: string | ThemeColor;
opacity?: string;
margin?: string;
padding?: string;

View File

@@ -62,7 +62,7 @@ export namespace EditorContextKeys {
export const hasReferenceProvider = new RawContextKey<boolean>('editorHasReferenceProvider', false, nls.localize('editorHasReferenceProvider', "Whether the editor has a reference provider"));
export const hasRenameProvider = new RawContextKey<boolean>('editorHasRenameProvider', false, nls.localize('editorHasRenameProvider', "Whether the editor has a rename provider"));
export const hasSignatureHelpProvider = new RawContextKey<boolean>('editorHasSignatureHelpProvider', false, nls.localize('editorHasSignatureHelpProvider', "Whether the editor has a signature help provider"));
export const hasInlineHintsProvider = new RawContextKey<boolean>('editorHasInlineHintsProvider', false, nls.localize('editorHasInlineHintsProvider', "Whether the editor has an inline hints provider"));
export const hasInlayHintsProvider = new RawContextKey<boolean>('editorHasInlayHintsProvider', false, nls.localize('editorHasInlayHintsProvider', "Whether the editor has an inline hints provider"));
// -- mode context keys: formatting
export const hasDocumentFormattingProvider = new RawContextKey<boolean>('editorHasDocumentFormattingProvider', false, nls.localize('editorHasDocumentFormattingProvider', "Whether the editor has a document formatting provider"));

View File

@@ -73,6 +73,11 @@ export interface IModelDecorationMinimapOptions extends IDecorationOptions {
* Options for a model decoration.
*/
export interface IModelDecorationOptions {
/**
* A debug description that can be used for inspecting model decorations.
* @internal
*/
description: string;
/**
* Customize the growing behavior of the decoration when typing at the edges of the decoration.
* Defaults to TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges
@@ -1220,7 +1225,6 @@ export interface ITextModel {
/**
* An event emitted when the model has been attached to the first editor or detached from the last editor.
* @event
* @internal
*/
onDidChangeAttached(listener: () => void): IDisposable;
/**
@@ -1247,7 +1251,6 @@ export interface ITextModel {
/**
* Returns if this model is attached to an editor or not.
* @internal
*/
isAttachedToEditor(): boolean;
@@ -1256,6 +1259,33 @@ export interface ITextModel {
* @internal
*/
getAttachedEditorCount(): number;
/**
* Among all positions that are projected to the same position in the underlying text model as
* the given position, select a unique position as indicated by the affinity.
* @internal
*/
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
/**
* @internal
*/
export const enum PositionNormalizationAffinity {
/**
* Prefers the left most position.
*/
Left = 0,
/**
* Prefers the right most position.
*/
Right = 1,
}
/**

View File

@@ -3028,6 +3028,30 @@ export class TextModel extends Disposable implements model.ITextModel {
}
//#endregion
normalizePosition(position: Position, affinity: model.PositionNormalizationAffinity): Position {
return position;
}
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
public getLineIndentColumn(lineNumber: number): number {
// Columns start with 1.
return indentOfLine(this.getLineContent(lineNumber)) + 1;
}
}
function indentOfLine(line: string): number {
let indent = 0;
for (const c of line) {
if (c === ' ' || c === '\t') {
indent++;
} else {
break;
}
}
return indent;
}
//#region Decorations
@@ -3205,6 +3229,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
return new ModelDecorationOptions(options);
}
readonly description: string;
readonly stickiness: model.TrackedRangeStickiness;
readonly zIndex: number;
readonly className: string | null;
@@ -3225,6 +3250,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
readonly afterContentClassName: string | null;
private constructor(options: model.IModelDecorationOptions) {
this.description = options.description;
this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
this.zIndex = options.zIndex || 0;
this.className = options.className ? cleanClassName(options.className) : null;
@@ -3245,16 +3271,16 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;
}
}
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({});
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });
/**
* The order carefully matches the values of the enum.
*/
const TRACKED_RANGE_OPTIONS = [
ModelDecorationOptions.register({ stickiness: model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }),
ModelDecorationOptions.register({ stickiness: model.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }),
ModelDecorationOptions.register({ stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore }),
ModelDecorationOptions.register({ stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter }),
ModelDecorationOptions.register({ description: 'tracked-range-always-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }),
ModelDecorationOptions.register({ description: 'tracked-range-never-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }),
ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-before', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore }),
ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-after', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter }),
];
function _normalizeOptions(options: model.IModelDecorationOptions): ModelDecorationOptions {

View File

@@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Position } from 'vs/editor/common/core/position';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token';
@@ -227,7 +227,7 @@ export interface IState {
}
/**
* A provider result represents the values a provider, like the [`HoverProvider`](#HoverProvider),
* A provider result represents the values a provider, like the {@link HoverProvider},
* may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves
* to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a
* thenable.
@@ -557,13 +557,13 @@ export interface CompletionItem {
documentation?: string | IMarkdownString;
/**
* A string that should be used when comparing this item
* with other items. When `falsy` the [label](#CompletionItem.label)
* with other items. When `falsy` the {@link CompletionItem.label label}
* is used.
*/
sortText?: string;
/**
* A string that should be used when filtering a set of
* completion items. When `falsy` the [label](#CompletionItem.label)
* completion items. When `falsy` the {@link CompletionItem.label label}
* is used.
*/
filterText?: string;
@@ -587,11 +587,11 @@ export interface CompletionItem {
/**
* A range of text that should be replaced by this completion item.
*
* Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the
* Defaults to a range from the start of the {@link TextDocument.getWordRangeAtPosition current word} to the
* current position.
*
* *Note:* The range must be a [single line](#Range.isSingleLine) and it must
* [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems).
* *Note:* The range must be a {@link Range.isSingleLine single line} and it must
* {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}.
*/
range: IRange | { insert: IRange, replace: IRange };
/**
@@ -638,7 +638,7 @@ export const enum CompletionTriggerKind {
}
/**
* Contains additional information about the context in which
* [completion provider](#CompletionItemProvider.provideCompletionItems) is triggered.
* {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered.
*/
export interface CompletionContext {
/**
@@ -658,10 +658,10 @@ export interface CompletionContext {
*
* When computing *complete* completion items is expensive, providers can optionally implement
* the `resolveCompletionItem`-function. In that case it is enough to return completion
* items with a [label](#CompletionItem.label) from the
* [provideCompletionItems](#CompletionItemProvider.provideCompletionItems)-function. Subsequently,
* items with a {@link CompletionItem.label label} from the
* {@link CompletionItemProvider.provideCompletionItems provideCompletionItems}-function. Subsequently,
* when a completion item is shown in the UI and gains focus this provider is asked to resolve
* the item, like adding [doc-comment](#CompletionItem.documentation) or [details](#CompletionItem.detail).
* the item, like adding {@link CompletionItem.documentation doc-comment} or {@link CompletionItem.detail details}.
*/
export interface CompletionItemProvider {
@@ -677,14 +677,73 @@ export interface CompletionItemProvider {
provideCompletionItems(model: model.ITextModel, position: Position, context: CompletionContext, token: CancellationToken): ProviderResult<CompletionList>;
/**
* Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation)
* or [details](#CompletionItem.detail).
* Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment}
* or {@link CompletionItem.detail details}.
*
* The editor will only resolve a completion item once.
*/
resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult<CompletionItem>;
}
/**
* How an {@link InlineCompletionsProvider inline completion provider} was triggered.
*/
export enum InlineCompletionTriggerKind {
/**
* Completion was triggered automatically while editing.
* It is sufficient to return a single completion item in this case.
*/
Automatic = 0,
/**
* Completion was triggered explicitly by a user gesture.
* Return multiple completion items to enable cycling through them.
*/
Explicit = 1,
}
export interface InlineCompletionContext {
/**
* How the completion was triggered.
*/
readonly triggerKind: InlineCompletionTriggerKind;
}
export interface InlineCompletion {
/**
* The text to insert.
* If the text contains a line break, the range must end at the end of a line.
* If existing text should be replaced, the existing text must be a prefix of the text to insert.
*/
readonly text: string;
/**
* The range to replace.
* Must begin and end on the same line.
*/
readonly range?: IRange;
readonly command?: Command;
}
export interface InlineCompletions<TItem extends InlineCompletion = InlineCompletion> {
readonly items: readonly TItem[];
}
export interface InlineCompletionsProvider<T extends InlineCompletions = InlineCompletions> {
provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult<T>;
/**
* Will be called when an item is shown.
*/
handleItemDidShow?(completions: T, item: T['items'][number]): void;
/**
* Will be called when a completions list is no longer in use and can be garbage-collected.
*/
freeInlineCompletions(completions: T): void;
}
export interface CodeAction {
title: string;
command?: Command;
@@ -870,7 +929,7 @@ export interface DocumentHighlight {
*/
range: IRange;
/**
* The highlight kind, default is [text](#DocumentHighlightKind.Text).
* The highlight kind, default is {@link DocumentHighlightKind.Text text}.
*/
kind?: DocumentHighlightKind;
}
@@ -1323,12 +1382,12 @@ export interface IColorPresentation {
*/
label: string;
/**
* An [edit](#TextEdit) which is applied to a document when selecting
* An {@link TextEdit edit} which is applied to a document when selecting
* this presentation for the color.
*/
textEdit?: TextEdit;
/**
* An optional array of additional [text edits](#TextEdit) that are applied when
* An optional array of additional {@link TextEdit text edits} that are applied when
* selecting this color presentation.
*/
additionalTextEdits?: TextEdit[];
@@ -1406,10 +1465,10 @@ export interface FoldingRange {
end: number;
/**
* Describes the [Kind](#FoldingRangeKind) of the folding range such as [Comment](#FoldingRangeKind.Comment) or
* [Region](#FoldingRangeKind.Region). The kind is used to categorize folding ranges and used by commands
* Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or
* {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands
* like 'Fold all comments'. See
* [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds.
* {@link FoldingRangeKind} for an enumeration of standardized kinds.
*/
kind?: FoldingRangeKind;
}
@@ -1429,7 +1488,7 @@ export class FoldingRangeKind {
static readonly Region = new FoldingRangeKind('region');
/**
* Creates a new [FoldingRangeKind](#FoldingRangeKind).
* Creates a new {@link FoldingRangeKind}.
*
* @param value of the kind.
*/
@@ -1701,24 +1760,23 @@ export interface CodeLensProvider {
}
export enum InlineHintKind {
export enum InlayHintKind {
Other = 0,
Type = 1,
Parameter = 2,
}
export interface InlineHint {
export interface InlayHint {
text: string;
range: IRange;
kind: InlineHintKind;
description?: string | IMarkdownString;
position: IPosition;
kind: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export interface InlineHintsProvider {
onDidChangeInlineHints?: Event<void> | undefined;
provideInlineHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult<InlineHint[]>;
export interface InlayHintsProvider {
onDidChangeInlayHints?: Event<void> | undefined;
provideInlayHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult<InlayHint[]>;
}
export interface SemanticTokensLegend {
@@ -1771,6 +1829,11 @@ export const RenameProviderRegistry = new LanguageFeatureRegistry<RenameProvider
*/
export const CompletionProviderRegistry = new LanguageFeatureRegistry<CompletionItemProvider>();
/**
* @internal
*/
export const InlineCompletionsProviderRegistry = new LanguageFeatureRegistry<InlineCompletionsProvider>();
/**
* @internal
*/
@@ -1834,7 +1897,7 @@ export const CodeLensProviderRegistry = new LanguageFeatureRegistry<CodeLensProv
/**
* @internal
*/
export const InlineHintsProviderRegistry = new LanguageFeatureRegistry<InlineHintsProvider>();
export const InlayHintsProviderRegistry = new LanguageFeatureRegistry<InlayHintsProvider>();
/**
* @internal

View File

@@ -154,7 +154,7 @@ function getClassifier(): CharacterClassifier<CharacterClass> {
if (_classifier === null) {
_classifier = new CharacterClassifier<CharacterClass>(CharacterClass.None);
const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;‘“〈《「『〔([{「」}])〕』」》〉”’`~…';
const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;‘「『〔([{「」}])〕』」’`~…';
for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) {
_classifier.set(FORCE_TERMINATION_CHARACTERS.charCodeAt(i), CharacterClass.ForceTermination);
}

View File

@@ -64,7 +64,12 @@ export class OnEnterSupport {
reg: rule.previousLineText,
text: previousLineText
}].every((obj): boolean => {
return obj.reg ? obj.reg.test(obj.text) : true;
if (!obj.reg) {
return true;
}
obj.reg.lastIndex = 0; // To disable the effect of the "g" flag.
return obj.reg.test(obj.text);
});
if (regResult) {

View File

@@ -239,6 +239,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
}
return {
description: 'marker-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className,
showIfCollapsed: true,

View File

@@ -219,82 +219,84 @@ export enum EditorOption {
highlightActiveIndentGuide = 49,
hover = 50,
inDiffEditor = 51,
letterSpacing = 52,
lightbulb = 53,
lineDecorationsWidth = 54,
lineHeight = 55,
lineNumbers = 56,
lineNumbersMinChars = 57,
linkedEditing = 58,
links = 59,
matchBrackets = 60,
minimap = 61,
mouseStyle = 62,
mouseWheelScrollSensitivity = 63,
mouseWheelZoom = 64,
multiCursorMergeOverlapping = 65,
multiCursorModifier = 66,
multiCursorPaste = 67,
occurrencesHighlight = 68,
overviewRulerBorder = 69,
overviewRulerLanes = 70,
padding = 71,
parameterHints = 72,
peekWidgetDefaultFocus = 73,
definitionLinkOpensInPeek = 74,
quickSuggestions = 75,
quickSuggestionsDelay = 76,
readOnly = 77,
renameOnType = 78,
renderControlCharacters = 79,
renderIndentGuides = 80,
renderFinalNewline = 81,
renderLineHighlight = 82,
renderLineHighlightOnlyWhenFocus = 83,
renderValidationDecorations = 84,
renderWhitespace = 85,
revealHorizontalRightPadding = 86,
roundedSelection = 87,
rulers = 88,
scrollbar = 89,
scrollBeyondLastColumn = 90,
scrollBeyondLastLine = 91,
scrollPredominantAxis = 92,
selectionClipboard = 93,
selectionHighlight = 94,
selectOnLineNumbers = 95,
showFoldingControls = 96,
showUnused = 97,
snippetSuggestions = 98,
smartSelect = 99,
smoothScrolling = 100,
stickyTabStops = 101,
stopRenderingLineAfter = 102,
suggest = 103,
suggestFontSize = 104,
suggestLineHeight = 105,
suggestOnTriggerCharacters = 106,
suggestSelection = 107,
tabCompletion = 108,
tabIndex = 109,
unusualLineTerminators = 110,
useTabStops = 111,
wordSeparators = 112,
wordWrap = 113,
wordWrapBreakAfterCharacters = 114,
wordWrapBreakBeforeCharacters = 115,
wordWrapColumn = 116,
wordWrapOverride1 = 117,
wordWrapOverride2 = 118,
wrappingIndent = 119,
wrappingStrategy = 120,
showDeprecated = 121,
inlineHints = 122,
editorClassName = 123,
pixelRatio = 124,
tabFocusMode = 125,
layoutInfo = 126,
wrappingInfo = 127
inlineSuggest = 52,
letterSpacing = 53,
lightbulb = 54,
lineDecorationsWidth = 55,
lineHeight = 56,
lineNumbers = 57,
lineNumbersMinChars = 58,
linkedEditing = 59,
links = 60,
matchBrackets = 61,
minimap = 62,
mouseStyle = 63,
mouseWheelScrollSensitivity = 64,
mouseWheelZoom = 65,
multiCursorMergeOverlapping = 66,
multiCursorModifier = 67,
multiCursorPaste = 68,
occurrencesHighlight = 69,
overviewRulerBorder = 70,
overviewRulerLanes = 71,
padding = 72,
parameterHints = 73,
peekWidgetDefaultFocus = 74,
definitionLinkOpensInPeek = 75,
quickSuggestions = 76,
quickSuggestionsDelay = 77,
readOnly = 78,
renameOnType = 79,
renderControlCharacters = 80,
renderIndentGuides = 81,
renderFinalNewline = 82,
renderLineHighlight = 83,
renderLineHighlightOnlyWhenFocus = 84,
renderValidationDecorations = 85,
renderWhitespace = 86,
revealHorizontalRightPadding = 87,
roundedSelection = 88,
rulers = 89,
scrollbar = 90,
scrollBeyondLastColumn = 91,
scrollBeyondLastLine = 92,
scrollPredominantAxis = 93,
selectionClipboard = 94,
selectionHighlight = 95,
selectOnLineNumbers = 96,
showFoldingControls = 97,
showUnused = 98,
snippetSuggestions = 99,
smartSelect = 100,
smoothScrolling = 101,
stickyTabStops = 102,
stopRenderingLineAfter = 103,
suggest = 104,
suggestFontSize = 105,
suggestLineHeight = 106,
suggestOnTriggerCharacters = 107,
suggestSelection = 108,
tabCompletion = 109,
tabIndex = 110,
unusualLineTerminators = 111,
useShadowDOM = 112,
useTabStops = 113,
wordSeparators = 114,
wordWrap = 115,
wordWrapBreakAfterCharacters = 116,
wordWrapBreakBeforeCharacters = 117,
wordWrapColumn = 118,
wordWrapOverride1 = 119,
wordWrapOverride2 = 120,
wrappingIndent = 121,
wrappingStrategy = 122,
showDeprecated = 123,
inlayHints = 124,
editorClassName = 125,
pixelRatio = 126,
tabFocusMode = 127,
layoutInfo = 128,
wrappingInfo = 129
}
/**
@@ -353,12 +355,28 @@ export enum IndentAction {
Outdent = 3
}
export enum InlineHintKind {
export enum InlayHintKind {
Other = 0,
Type = 1,
Parameter = 2
}
/**
* How an {@link InlineCompletionsProvider inline completion provider} was triggered.
*/
export enum InlineCompletionTriggerKind {
/**
* Completion was triggered automatically while editing.
* It is sufficient to return a single completion item in this case.
*/
Automatic = 0,
/**
* Completion was triggered explicitly by a user gesture.
* Return multiple completion items to enable cycling through them.
*/
Explicit = 1
}
/**
* Virtual Key Codes, the value does not hold any inherent meaning.
* Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
@@ -823,4 +841,4 @@ export enum WrappingIndent {
* DeepIndent => wrapped lines get +2 indentation toward the parent.
*/
DeepIndent = 3
}
}

View File

@@ -43,6 +43,9 @@ export const editorGutter = registerColor('editorGutter.background', { dark: edi
export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hc: Color.fromHex('#fff').transparent(0.8) }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.'));
export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.'));
export const ghostTextBorder = registerColor('editorGhostText.border', { dark: null, light: null, hc: Color.fromHex('#fff').transparent(0.8) }, nls.localize('editorGhostTextBorder', 'Border color of ghost text in the editor.'));
export const ghostTextForeground = registerColor('editorGhostText.foreground', { dark: Color.fromHex('#ffffff56'), light: Color.fromHex('#0007'), hc: null }, nls.localize('editorGhostTextForeground', 'Foreground color of the ghost text in the editor.'));
const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6));
export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true);
export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.'));

View File

@@ -47,6 +47,10 @@ class LinePart {
public isWhitespace(): boolean {
return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false);
}
public isPseudoAfter(): boolean {
return (this.metadata & LinePartMetadata.PSEUDO_AFTER_MASK ? true : false);
}
}
export class LineRange {
@@ -213,16 +217,23 @@ export const enum CharacterMappingConstants {
PART_INDEX_OFFSET = 16
}
export class DomPosition {
constructor(
public readonly partIndex: number,
public readonly charIndex: number
) { }
}
/**
* Provides a both direction mapping between a line's character and its rendered position.
*/
export class CharacterMapping {
public static getPartIndex(partData: number): number {
private static getPartIndex(partData: number): number {
return (partData & CharacterMappingConstants.PART_INDEX_MASK) >>> CharacterMappingConstants.PART_INDEX_OFFSET;
}
public static getCharIndex(partData: number): number {
private static getCharIndex(partData: number): number {
return (partData & CharacterMappingConstants.CHAR_INDEX_MASK) >>> CharacterMappingConstants.CHAR_INDEX_OFFSET;
}
@@ -236,20 +247,24 @@ export class CharacterMapping {
this._absoluteOffsets = new Uint32Array(this.length);
}
public setPartData(charOffset: number, partIndex: number, charIndex: number, partAbsoluteOffset: number): void {
let partData = (
public setColumnInfo(column: number, partIndex: number, charIndex: number, partAbsoluteOffset: number): void {
const partData = (
(partIndex << CharacterMappingConstants.PART_INDEX_OFFSET)
| (charIndex << CharacterMappingConstants.CHAR_INDEX_OFFSET)
) >>> 0;
this._data[charOffset] = partData;
this._absoluteOffsets[charOffset] = partAbsoluteOffset + charIndex;
this._data[column - 1] = partData;
this._absoluteOffsets[column - 1] = partAbsoluteOffset + charIndex;
}
public getAbsoluteOffsets(): Uint32Array {
return this._absoluteOffsets;
public getAbsoluteOffset(column: number): number {
if (this._absoluteOffsets.length === 0) {
// No characters on this line
return 0;
}
return this._absoluteOffsets[column - 1];
}
public charOffsetToPartData(charOffset: number): number {
private charOffsetToPartData(charOffset: number): number {
if (this.length === 0) {
return 0;
}
@@ -262,7 +277,19 @@ export class CharacterMapping {
return this._data[charOffset];
}
public partDataToCharOffset(partIndex: number, partLength: number, charIndex: number): number {
public getDomPosition(column: number): DomPosition {
const partData = this.charOffsetToPartData(column - 1);
const partIndex = CharacterMapping.getPartIndex(partData);
const charIndex = CharacterMapping.getCharIndex(partData);
return new DomPosition(partIndex, charIndex);
}
public getColumn(domPosition: DomPosition, partLength: number): number {
const charOffset = this.partDataToCharOffset(domPosition.partIndex, partLength, domPosition.charIndex);
return charOffset + 1;
}
private partDataToCharOffset(partIndex: number, partLength: number, charIndex: number): number {
if (this.length === 0) {
return 0;
}
@@ -373,7 +400,7 @@ export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): Rend
sb.appendASCIIString(`</span>`);
const characterMapping = new CharacterMapping(1, beforeCount + afterCount);
characterMapping.setPartData(0, beforeCount, 0, 0);
characterMapping.setColumnInfo(1, beforeCount, 0, 0);
return new RenderLineOutput(
characterMapping,
@@ -792,14 +819,11 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP
const lastTokenEndIndex = tokens[tokens.length - 1].endIndex;
if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
let classNames: string[] = [];
let metadata = 0;
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
classNames.push(lineDecorations[lineDecorationIndex].className);
metadata |= lineDecorations[lineDecorationIndex].metadata;
const lineDecoration = lineDecorations[lineDecorationIndex];
result[resultLen++] = new LinePart(lastResultEndIndex, lineDecoration.className, lineDecoration.metadata);
lineDecorationIndex++;
}
result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '), metadata);
}
return result;
@@ -827,6 +851,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const renderControlCharacters = input.renderControlCharacters;
const characterMapping = new CharacterMapping(len + 1, parts.length);
let lastCharacterMappingDefined = false;
let charIndex = 0;
let visibleColumn = startVisibleColumn;
@@ -850,7 +875,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const partType = part.type;
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace());
const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements);
const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.metadata === LinePartMetadata.PSEUDO_AFTER);
const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.isPseudoAfter());
charOffsetInPart = 0;
sb.appendASCIIString('<span class="');
@@ -882,7 +907,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
let charWidth: number;
@@ -920,7 +945,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
sb.appendASCII(CharCode.GreaterThan);
for (; charIndex < partEndIndex; charIndex++) {
characterMapping.setPartData(charIndex, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
characterMapping.setColumnInfo(charIndex + 1, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset);
partDisplacement = 0;
const charCode = lineContent.charCodeAt(charIndex);
@@ -999,13 +1024,20 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
partDisplacement = 0;
}
if (charIndex >= len && !lastCharacterMappingDefined && part.isPseudoAfter()) {
lastCharacterMappingDefined = true;
characterMapping.setColumnInfo(charIndex + 1, partIndex, charOffsetInPart, partAbsoluteOffset);
}
sb.appendASCIIString('</span>');
}
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart, partAbsoluteOffset);
if (!lastCharacterMappingDefined) {
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
characterMapping.setColumnInfo(len + 1, parts.length - 1, charOffsetInPart, partAbsoluteOffset);
}
if (isOverflowing) {
sb.appendASCIIString('<span>&hellip;</span>');

View File

@@ -8,7 +8,7 @@ import { WrappingIndent } from 'vs/editor/common/config/editorOptions';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDeltaDecoration, ITextModel, PositionNormalizationAffinity } from 'vs/editor/common/model';
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';
@@ -46,6 +46,7 @@ export interface ISplitLine {
getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number;
getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position;
getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number;
normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionNormalizationAffinity): Position;
}
export interface IViewModelLinesCollection extends IDisposable {
@@ -75,6 +76,13 @@ export interface IViewModelLinesCollection extends IDisposable {
getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations;
getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[];
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
export class CoordinatesConverter implements ICoordinatesConverter {
@@ -971,6 +979,31 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
return finalResult;
}
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position {
const viewLineNumber = this._toValidViewLineNumber(position.lineNumber);
const r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
const lineIndex = r.index;
const remainder = r.remainder;
return this.lines[lineIndex].normalizePosition(this.model, lineIndex + 1, remainder, position, affinity);
}
public getLineIndentColumn(lineNumber: number): number {
const viewLineNumber = this._toValidViewLineNumber(lineNumber);
const r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
const lineIndex = r.index;
const remainder = r.remainder;
if (remainder === 0) {
return this.model.getLineIndentColumn(lineIndex + 1);
}
// wrapped lines have no indentation.
// We deliberately don't handle the case that indentation is wrapped
// to avoid two view lines reporting indentation for the very same model line.
return 0;
}
}
class VisibleIdentitySplitLine implements ISplitLine {
@@ -1046,6 +1079,10 @@ class VisibleIdentitySplitLine implements ISplitLine {
public getViewLineNumberOfModelPosition(deltaLineNumber: number, _inputColumn: number): number {
return deltaLineNumber;
}
public normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionNormalizationAffinity): Position {
return outputPosition;
}
}
class InvisibleIdentitySplitLine implements ISplitLine {
@@ -1108,6 +1145,10 @@ class InvisibleIdentitySplitLine implements ISplitLine {
public getViewLineNumberOfModelPosition(_deltaLineNumber: number, _inputColumn: number): number {
throw new Error('Not supported');
}
public normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionNormalizationAffinity): Position {
throw new Error('Not supported');
}
}
export class SplitLine implements ISplitLine {
@@ -1190,6 +1231,10 @@ export class SplitLine implements ISplitLine {
if (!this._isVisible) {
throw new Error('Not supported');
}
return this._getViewLineMinColumn(outputLineIndex);
}
private _getViewLineMinColumn(outputLineIndex: number): number {
if (outputLineIndex > 0) {
return this._lineBreakData.wrappedTextIndentLength + 1;
}
@@ -1200,7 +1245,7 @@ export class SplitLine implements ISplitLine {
if (!this._isVisible) {
throw new Error('Not supported');
}
return this.getViewLineContent(model, modelLineNumber, outputLineIndex).length + 1;
return this.getViewLineLength(model, modelLineNumber, outputLineIndex) + 1;
}
public getViewLineData(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): ViewLineData {
@@ -1298,6 +1343,21 @@ export class SplitLine implements ISplitLine {
const r = LineBreakData.getOutputPositionOfInputOffset(this._lineBreakData.breakOffsets, inputColumn - 1);
return (deltaLineNumber + r.outputLineIndex);
}
public normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionNormalizationAffinity): Position {
if (affinity === PositionNormalizationAffinity.Left) {
if (outputLineIndex > 0 && outputPosition.column === this._getViewLineMinColumn(outputLineIndex)) {
return new Position(outputPosition.lineNumber - 1, this.getViewLineMaxColumn(model, modelLineNumber, outputLineIndex - 1));
}
}
else if (affinity === PositionNormalizationAffinity.Right) {
const maxOutputLineIndex = this.getViewLineCount() - 1;
if (outputLineIndex < maxOutputLineIndex && outputPosition.column === this.getViewLineMaxColumn(model, modelLineNumber, outputLineIndex)) {
return new Position(outputPosition.lineNumber + 1, this._getViewLineMinColumn(outputLineIndex + 1));
}
}
return outputPosition;
}
}
let _spaces: string[] = [''];
@@ -1532,6 +1592,14 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[] {
return this.model.getDecorationsInRange(range, ownerId, filterOutValidation);
}
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position {
return this.model.normalizePosition(position, affinity);
}
public getLineIndentColumn(lineNumber: number): number {
return this.model.getLineIndentColumn(lineNumber);
}
}
class OverviewRulerDecorations {

View File

@@ -12,7 +12,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IRange, Range } from 'vs/editor/common/core/range';
import { IConfiguration, IViewState, ScrollType, ICursorState, ICommand, INewScrollPosition } from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions, IIdentifiedSingleEditOperation, ICursorStateComputer } from 'vs/editor/common/model';
import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions, IIdentifiedSingleEditOperation, ICursorStateComputer, PositionNormalizationAffinity } from 'vs/editor/common/model';
import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes';
@@ -1037,4 +1037,16 @@ export class ViewModel extends Disposable implements IViewModel {
this._eventDispatcher.endEmitViewEvents();
}
}
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position {
return this._lines.normalizePosition(position, affinity);
}
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number {
return this._lines.getLineIndentColumn(lineNumber);
}
}