Skip to content

Commit b1fbe70

Browse files
committed
Introduce cursorCommon
1 parent 0584e71 commit b1fbe70

12 files changed

Lines changed: 293 additions & 344 deletions

src/vs/editor/common/commands/shiftCommand.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict';
66

77
import * as strings from 'vs/base/common/strings';
8-
import { CursorMoveHelper } from 'vs/editor/common/controller/cursorMoveHelper';
8+
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
99
import { Range } from 'vs/editor/common/core/range';
1010
import { Selection } from 'vs/editor/common/core/selection';
1111
import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
@@ -22,19 +22,19 @@ export class ShiftCommand implements ICommand {
2222

2323
public static unshiftIndentCount(line: string, column: number, tabSize: number): number {
2424
// Determine the visible column where the content starts
25-
let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize);
25+
let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize);
2626

27-
let desiredTabStop = CursorMoveHelper.prevTabColumn(contentStartVisibleColumn, tabSize);
27+
let desiredTabStop = CursorColumns.prevTabStop(contentStartVisibleColumn, tabSize);
2828

2929
// The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents
3030
return desiredTabStop / tabSize;
3131
}
3232

3333
public static shiftIndentCount(line: string, column: number, tabSize: number): number {
3434
// Determine the visible column where the content starts
35-
let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize);
35+
let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize);
3636

37-
let desiredTabStop = CursorMoveHelper.nextTabColumn(contentStartVisibleColumn, tabSize);
37+
let desiredTabStop = CursorColumns.nextTabStop(contentStartVisibleColumn, tabSize);
3838

3939
// The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents
4040
return desiredTabStop / tabSize;
@@ -97,7 +97,7 @@ export class ShiftCommand implements ICommand {
9797
}
9898

9999
if (lineNumber > 1) {
100-
let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(lineText, indentationEndIndex + 1, tabSize);
100+
let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(lineText, indentationEndIndex + 1, tabSize);
101101
if (contentStartVisibleColumn % tabSize !== 0) {
102102
// The current line is "miss-aligned", so let's see if this is expected...
103103
// This can only happen when it has trailing commas in the indent

src/vs/editor/common/commonCodeEditor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyServiceTarget, IContextKeyServi
1515
import { CommonEditorConfiguration } from 'vs/editor/common/config/commonEditorConfig';
1616
import { DefaultConfig } from 'vs/editor/common/config/defaultConfig';
1717
import { Cursor } from 'vs/editor/common/controller/cursor';
18-
import { CursorMoveHelper } from 'vs/editor/common/controller/cursorMoveHelper';
18+
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
1919
import { IViewModelHelper } from 'vs/editor/common/controller/oneCursor';
2020
import { EditorState } from 'vs/editor/common/core/editorState';
2121
import { Position } from 'vs/editor/common/core/position';
@@ -229,7 +229,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
229229
let position = this.model.validatePosition(rawPosition);
230230
let tabSize = this.model.getOptions().tabSize;
231231

232-
return CursorMoveHelper.visibleColumnFromColumn(this.model, position.lineNumber, position.column, tabSize) + 1;
232+
return CursorColumns.visibleColumnFromColumn(this.model.getLineContent(position.lineNumber), position.column, tabSize) + 1;
233233
}
234234

235235
public getPosition(): Position {

src/vs/editor/common/controller/cursor.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import { Position } from 'vs/editor/common/core/position';
1616
import { Range } from 'vs/editor/common/core/range';
1717
import { Selection, SelectionDirection } from 'vs/editor/common/core/selection';
1818
import * as editorCommon from 'vs/editor/common/editorCommon';
19-
import { IColumnSelectResult, CursorMove } from 'vs/editor/common/controller/cursorMoveHelper';
19+
import { IColumnSelectResult } from 'vs/editor/common/controller/cursorMoveHelper';
20+
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
2021
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
2122
import { WordNavigationType } from 'vs/editor/common/controller/cursorWordOperations';
2223

@@ -1127,7 +1128,7 @@ export class Cursor extends EventEmitter {
11271128
if (!this._columnSelectToVisualColumn) {
11281129
let primaryCursor = this.cursors.getAll()[0];
11291130
let primaryPos = primaryCursor.viewState.position;
1130-
return CursorMove.visibleColumnFromColumn2(primaryCursor.config, primaryCursor.viewModel, primaryPos);
1131+
return CursorColumns.visibleColumnFromColumn2(primaryCursor.config, primaryCursor.viewModel, primaryPos);
11311132
}
11321133
return this._columnSelectToVisualColumn;
11331134
}

src/vs/editor/common/controller/cursorColumnSelection.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
import { Selection } from 'vs/editor/common/core/selection';
88
import { Position } from 'vs/editor/common/core/position';
9-
import { CursorMove, CursorMoveConfiguration, ICursorMoveHelperModel } from 'vs/editor/common/controller/cursorMoveHelper';
9+
import { CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
1010

1111
export interface IViewColumnSelectResult {
1212
viewSelections: Selection[];
1313
reversed: boolean;
1414
}
1515

1616
export class ColumnSelection {
17-
public static columnSelect(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, fromLineNumber: number, fromVisibleColumn: number, toLineNumber: number, toVisibleColumn: number): IViewColumnSelectResult {
17+
public static columnSelect(config: CursorConfiguration, model: ICursorSimpleModel, fromLineNumber: number, fromVisibleColumn: number, toLineNumber: number, toVisibleColumn: number): IViewColumnSelectResult {
1818
let lineCount = Math.abs(toLineNumber - fromLineNumber) + 1;
1919
let reversed = (fromLineNumber > toLineNumber);
2020
let isRTL = (fromVisibleColumn > toVisibleColumn);
@@ -27,10 +27,10 @@ export class ColumnSelection {
2727
for (let i = 0; i < lineCount; i++) {
2828
let lineNumber = fromLineNumber + (reversed ? -i : i);
2929

30-
let startColumn = CursorMove.columnFromVisibleColumn2(config, model, lineNumber, fromVisibleColumn);
31-
let endColumn = CursorMove.columnFromVisibleColumn2(config, model, lineNumber, toVisibleColumn);
32-
let visibleStartColumn = CursorMove.visibleColumnFromColumn2(config, model, new Position(lineNumber, startColumn));
33-
let visibleEndColumn = CursorMove.visibleColumnFromColumn2(config, model, new Position(lineNumber, endColumn));
30+
let startColumn = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, fromVisibleColumn);
31+
let endColumn = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, toVisibleColumn);
32+
let visibleStartColumn = CursorColumns.visibleColumnFromColumn2(config, model, new Position(lineNumber, startColumn));
33+
let visibleEndColumn = CursorColumns.visibleColumnFromColumn2(config, model, new Position(lineNumber, endColumn));
3434

3535
// console.log(`lineNumber: ${lineNumber}: visibleStartColumn: ${visibleStartColumn}, visibleEndColumn: ${visibleEndColumn}`);
3636

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
'use strict';
6+
7+
import { Position } from 'vs/editor/common/core/position';
8+
import { CharCode } from 'vs/base/common/charCode';
9+
import * as strings from 'vs/base/common/strings';
10+
import { IModeConfiguration } from 'vs/editor/common/controller/oneCursor';
11+
import { IConfigurationChangedEvent, TextModelResolvedOptions, IConfiguration } from 'vs/editor/common/editorCommon';
12+
import { TextModel } from 'vs/editor/common/model/textModel';
13+
14+
export interface CharacterMap {
15+
[char: string]: string;
16+
}
17+
18+
export class CursorConfiguration {
19+
_cursorMoveConfigurationBrand: void;
20+
21+
public readonly tabSize: number;
22+
public readonly insertSpaces: boolean;
23+
public readonly oneIndent: string;
24+
public readonly pageSize: number;
25+
public readonly useTabStops: boolean;
26+
public readonly wordSeparators: string;
27+
public readonly autoClosingBrackets: boolean;
28+
public readonly autoClosingPairsOpen: CharacterMap;
29+
30+
public static shouldRecreate(e: IConfigurationChangedEvent): boolean {
31+
return (
32+
e.layoutInfo
33+
|| e.wordSeparators
34+
|| e.autoClosingBrackets
35+
|| e.useTabStops
36+
);
37+
}
38+
39+
constructor(
40+
oneIndent: string,
41+
modelOptions: TextModelResolvedOptions,
42+
configuration: IConfiguration,
43+
modeConfiguration: IModeConfiguration
44+
) {
45+
let c = configuration.editor;
46+
47+
this.tabSize = modelOptions.tabSize;
48+
this.insertSpaces = modelOptions.insertSpaces;
49+
this.oneIndent = oneIndent;
50+
this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2;
51+
this.useTabStops = c.useTabStops;
52+
this.wordSeparators = c.wordSeparators;
53+
this.autoClosingBrackets = c.autoClosingBrackets;
54+
this.autoClosingPairsOpen = modeConfiguration.autoClosingPairsOpen;
55+
}
56+
57+
public normalizeIndentation(str: string): string {
58+
return TextModel.normalizeIndentation(str, this.tabSize, this.insertSpaces);
59+
}
60+
}
61+
62+
export interface ICursorSimpleModel {
63+
getLineCount(): number;
64+
getLineContent(lineNumber: number): string;
65+
getLineMinColumn(lineNumber: number): number;
66+
getLineMaxColumn(lineNumber: number): number;
67+
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
68+
getLineLastNonWhitespaceColumn(lineNumber: number): number;
69+
}
70+
71+
/**
72+
* Common operations that work and make sense both on the model and on the view model.
73+
*/
74+
export class CursorColumns {
75+
76+
public static isLowSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean {
77+
let lineContent = model.getLineContent(lineNumber);
78+
if (charOffset < 0 || charOffset >= lineContent.length) {
79+
return false;
80+
}
81+
return strings.isLowSurrogate(lineContent.charCodeAt(charOffset));
82+
}
83+
84+
public static isHighSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean {
85+
let lineContent = model.getLineContent(lineNumber);
86+
if (charOffset < 0 || charOffset >= lineContent.length) {
87+
return false;
88+
}
89+
return strings.isHighSurrogate(lineContent.charCodeAt(charOffset));
90+
}
91+
92+
public static isInsideSurrogatePair(model: ICursorSimpleModel, lineNumber: number, column: number): boolean {
93+
return this.isHighSurrogate(model, lineNumber, column - 2);
94+
}
95+
96+
public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number {
97+
let endOffset = lineContent.length;
98+
if (endOffset > column - 1) {
99+
endOffset = column - 1;
100+
}
101+
102+
let result = 0;
103+
for (let i = 0; i < endOffset; i++) {
104+
let charCode = lineContent.charCodeAt(i);
105+
if (charCode === CharCode.Tab) {
106+
result = this.nextTabStop(result, tabSize);
107+
} else {
108+
result = result + 1;
109+
}
110+
}
111+
return result;
112+
}
113+
114+
public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number {
115+
return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize);
116+
}
117+
118+
public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number {
119+
if (visibleColumn <= 0) {
120+
return 1;
121+
}
122+
123+
const lineLength = lineContent.length;
124+
125+
let beforeVisibleColumn = 0;
126+
for (let i = 0; i < lineLength; i++) {
127+
let charCode = lineContent.charCodeAt(i);
128+
129+
let afterVisibleColumn: number;
130+
if (charCode === CharCode.Tab) {
131+
afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, tabSize);
132+
} else {
133+
afterVisibleColumn = beforeVisibleColumn + 1;
134+
}
135+
136+
if (afterVisibleColumn >= visibleColumn) {
137+
let prevDelta = visibleColumn - beforeVisibleColumn;
138+
let afterDelta = afterVisibleColumn - visibleColumn;
139+
if (afterDelta < prevDelta) {
140+
return i + 2;
141+
} else {
142+
return i + 1;
143+
}
144+
}
145+
146+
beforeVisibleColumn = afterVisibleColumn;
147+
}
148+
149+
// walked the entire string
150+
return lineLength + 1;
151+
}
152+
153+
public static columnFromVisibleColumn2(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, visibleColumn: number): number {
154+
let result = this.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.tabSize);
155+
156+
let minColumn = model.getLineMinColumn(lineNumber);
157+
if (result < minColumn) {
158+
return minColumn;
159+
}
160+
161+
let maxColumn = model.getLineMaxColumn(lineNumber);
162+
if (result > maxColumn) {
163+
return maxColumn;
164+
}
165+
166+
return result;
167+
}
168+
169+
/**
170+
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
171+
*/
172+
public static nextTabStop(visibleColumn: number, tabSize: number): number {
173+
return visibleColumn + tabSize - visibleColumn % tabSize;
174+
}
175+
176+
/**
177+
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
178+
*/
179+
public static prevTabStop(column: number, tabSize: number): number {
180+
return column - 1 - (column - 1) % tabSize;
181+
}
182+
}

src/vs/editor/common/controller/cursorDeleteOperations.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict';
66

77
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
8-
import { CursorMoveHelper, CursorMove, CursorMoveConfiguration, ICursorMoveHelperModel } from 'vs/editor/common/controller/cursorMoveHelper';
8+
import { CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
99
import { Range } from 'vs/editor/common/core/range';
1010
import { ICommand, CursorChangeReason } from 'vs/editor/common/editorCommon';
1111
import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations';
@@ -61,7 +61,7 @@ export class EditOperationResult {
6161

6262
export class DeleteOperations {
6363

64-
public static deleteRight(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
64+
public static deleteRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult {
6565

6666
let deleteSelection: Range = cursor.selection;
6767

@@ -92,7 +92,7 @@ export class DeleteOperations {
9292
});
9393
}
9494

95-
public static deleteAllRight(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
95+
public static deleteAllRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult {
9696
let selection = cursor.selection;
9797

9898
if (selection.isEmpty()) {
@@ -115,7 +115,7 @@ export class DeleteOperations {
115115
return this.deleteRight(config, model, cursor);
116116
}
117117

118-
public static autoClosingPairDelete(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
118+
public static autoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult {
119119
if (!config.autoClosingBrackets) {
120120
return null;
121121
}
@@ -150,7 +150,7 @@ export class DeleteOperations {
150150
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''));
151151
}
152152

153-
public static deleteLeft(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
153+
public static deleteLeft(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult {
154154
let r = this.autoClosingPairDelete(config, model, cursor);
155155
if (r) {
156156
// This was a case for an auto-closing pair delete
@@ -173,9 +173,9 @@ export class DeleteOperations {
173173
);
174174

175175
if (position.column <= lastIndentationColumn) {
176-
let fromVisibleColumn = CursorMove.visibleColumnFromColumn2(config, model, position);
177-
let toVisibleColumn = CursorMoveHelper.prevTabColumn(fromVisibleColumn, config.tabSize);
178-
let toColumn = CursorMove.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn);
176+
let fromVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, position);
177+
let toVisibleColumn = CursorColumns.prevTabStop(fromVisibleColumn, config.tabSize);
178+
let toColumn = CursorColumns.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn);
179179
deleteSelection = new Range(position.lineNumber, toColumn, position.lineNumber, position.column);
180180
} else {
181181
deleteSelection = new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column);
@@ -207,7 +207,7 @@ export class DeleteOperations {
207207
});
208208
}
209209

210-
public static deleteAllLeft(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
210+
public static deleteAllLeft(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult {
211211
let r = this.autoClosingPairDelete(config, model, cursor);
212212
if (r) {
213213
// This was a case for an auto-closing pair delete
@@ -235,7 +235,7 @@ export class DeleteOperations {
235235
return this.deleteLeft(config, model, cursor);
236236
}
237237

238-
public static cut(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState, enableEmptySelectionClipboard: boolean): EditOperationResult {
238+
public static cut(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState, enableEmptySelectionClipboard: boolean): EditOperationResult {
239239
let selection = cursor.selection;
240240

241241
if (selection.isEmpty()) {

0 commit comments

Comments
 (0)