Skip to content

Commit 0584e71

Browse files
committed
Move out more operations from oneCursor
1 parent d5bde7d commit 0584e71

6 files changed

Lines changed: 320 additions & 227 deletions

File tree

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

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
88
import { CursorMoveHelper, CursorMove, CursorMoveConfiguration, ICursorMoveHelperModel } from 'vs/editor/common/controller/cursorMoveHelper';
99
import { Range } from 'vs/editor/common/core/range';
10-
import { ICommand } from 'vs/editor/common/editorCommon';
10+
import { ICommand, CursorChangeReason } from 'vs/editor/common/editorCommon';
1111
import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations';
1212
import { CursorModelState } from 'vs/editor/common/controller/oneCursor';
1313
import * as strings from 'vs/base/common/strings';
@@ -19,17 +19,43 @@ export class EditOperationResult {
1919
readonly shouldPushStackElementBefore: boolean;
2020
readonly shouldPushStackElementAfter: boolean;
2121
readonly isAutoWhitespaceCommand: boolean;
22+
readonly shouldRevealHorizontal: boolean;
23+
readonly cursorPositionChangeReason: CursorChangeReason;
2224

2325
constructor(
2426
command: ICommand,
25-
shouldPushStackElementBefore: boolean,
26-
shouldPushStackElementAfter: boolean,
27-
isAutoWhitespaceCommand: boolean
27+
opts?: {
28+
shouldPushStackElementBefore: boolean;
29+
shouldPushStackElementAfter: boolean;
30+
isAutoWhitespaceCommand?: boolean;
31+
shouldRevealHorizontal?: boolean;
32+
cursorPositionChangeReason?: CursorChangeReason;
33+
}
2834
) {
2935
this.command = command;
30-
this.shouldPushStackElementBefore = shouldPushStackElementBefore;
31-
this.shouldPushStackElementAfter = shouldPushStackElementAfter;
32-
this.isAutoWhitespaceCommand = isAutoWhitespaceCommand;
36+
this.shouldPushStackElementBefore = false;
37+
this.shouldPushStackElementAfter = false;
38+
this.isAutoWhitespaceCommand = false;
39+
this.shouldRevealHorizontal = true;
40+
this.cursorPositionChangeReason = CursorChangeReason.NotSet;
41+
42+
if (typeof opts !== 'undefined') {
43+
if (typeof opts.shouldPushStackElementBefore !== 'undefined') {
44+
this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore;
45+
}
46+
if (typeof opts.shouldPushStackElementAfter !== 'undefined') {
47+
this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter;
48+
}
49+
if (typeof opts.isAutoWhitespaceCommand !== 'undefined') {
50+
this.isAutoWhitespaceCommand = opts.isAutoWhitespaceCommand;
51+
}
52+
if (typeof opts.shouldRevealHorizontal !== 'undefined') {
53+
this.shouldRevealHorizontal = opts.shouldRevealHorizontal;
54+
}
55+
if (typeof opts.cursorPositionChangeReason !== 'undefined') {
56+
this.cursorPositionChangeReason = opts.cursorPositionChangeReason;
57+
}
58+
}
3359
}
3460
}
3561

@@ -60,7 +86,10 @@ export class DeleteOperations {
6086
shouldPushStackElementBefore = true;
6187
}
6288

63-
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), shouldPushStackElementBefore, false, false);
89+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), {
90+
shouldPushStackElementBefore: shouldPushStackElementBefore,
91+
shouldPushStackElementAfter: false
92+
});
6493
}
6594

6695
public static deleteAllRight(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
@@ -79,7 +108,7 @@ export class DeleteOperations {
79108

80109
let deleteSelection = new Range(lineNumber, column, lineNumber, maxColumn);
81110
if (!deleteSelection.isEmpty()) {
82-
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), false, false, false);
111+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''));
83112
}
84113
}
85114

@@ -118,7 +147,7 @@ export class DeleteOperations {
118147
position.column + 1
119148
);
120149

121-
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), false, false, false);
150+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''));
122151
}
123152

124153
public static deleteLeft(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
@@ -172,7 +201,10 @@ export class DeleteOperations {
172201
shouldPushStackElementBefore = true;
173202
}
174203

175-
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), shouldPushStackElementBefore, false, false);
204+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), {
205+
shouldPushStackElementBefore: shouldPushStackElementBefore,
206+
shouldPushStackElementAfter: false
207+
});
176208
}
177209

178210
public static deleteAllLeft(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
@@ -196,11 +228,67 @@ export class DeleteOperations {
196228

197229
let deleteSelection = new Range(lineNumber, 1, lineNumber, column);
198230
if (!deleteSelection.isEmpty()) {
199-
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''), false, false, false);
231+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''));
200232
}
201233
}
202234

203235
return this.deleteLeft(config, model, cursor);
204236
}
205237

238+
public static cut(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState, enableEmptySelectionClipboard: boolean): EditOperationResult {
239+
let selection = cursor.selection;
240+
241+
if (selection.isEmpty()) {
242+
if (enableEmptySelectionClipboard) {
243+
// This is a full line cut
244+
245+
let position = cursor.position;
246+
247+
let startLineNumber: number,
248+
startColumn: number,
249+
endLineNumber: number,
250+
endColumn: number;
251+
252+
if (position.lineNumber < model.getLineCount()) {
253+
// Cutting a line in the middle of the model
254+
startLineNumber = position.lineNumber;
255+
startColumn = 1;
256+
endLineNumber = position.lineNumber + 1;
257+
endColumn = 1;
258+
} else if (position.lineNumber > 1) {
259+
// Cutting the last line & there are more than 1 lines in the model
260+
startLineNumber = position.lineNumber - 1;
261+
startColumn = model.getLineMaxColumn(position.lineNumber - 1);
262+
endLineNumber = position.lineNumber;
263+
endColumn = model.getLineMaxColumn(position.lineNumber);
264+
} else {
265+
// Cutting the single line that the model contains
266+
startLineNumber = position.lineNumber;
267+
startColumn = 1;
268+
endLineNumber = position.lineNumber;
269+
endColumn = model.getLineMaxColumn(position.lineNumber);
270+
}
271+
272+
let deleteSelection = new Range(
273+
startLineNumber,
274+
startColumn,
275+
endLineNumber,
276+
endColumn
277+
);
278+
279+
if (!deleteSelection.isEmpty()) {
280+
return new EditOperationResult(new ReplaceCommand(deleteSelection, ''));
281+
} else {
282+
return null;
283+
}
284+
} else {
285+
// Cannot cut empty selection
286+
return null;
287+
}
288+
} else {
289+
// Delete left or right, they will both result in the selection being deleted
290+
return this.deleteRight(config, model, cursor);
291+
}
292+
}
293+
206294
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IViewColumnSelectResult } from 'vs/editor/common/controller/cursorColum
1111
import * as strings from 'vs/base/common/strings';
1212
import { IModeConfiguration } from 'vs/editor/common/controller/oneCursor';
1313
import { IConfigurationChangedEvent, TextModelResolvedOptions, IConfiguration } from 'vs/editor/common/editorCommon';
14+
import { TextModel } from 'vs/editor/common/model/textModel';
1415

1516
export interface CharacterMap {
1617
[char: string]: string;
@@ -20,6 +21,8 @@ export class CursorMoveConfiguration {
2021
_cursorMoveConfigurationBrand: void;
2122

2223
public readonly tabSize: number;
24+
public readonly insertSpaces: boolean;
25+
public readonly oneIndent: string;
2326
public readonly pageSize: number;
2427
public readonly useTabStops: boolean;
2528
public readonly wordSeparators: string;
@@ -36,19 +39,26 @@ export class CursorMoveConfiguration {
3639
}
3740

3841
constructor(
42+
oneIndent: string,
3943
modelOptions: TextModelResolvedOptions,
4044
configuration: IConfiguration,
4145
modeConfiguration: IModeConfiguration
4246
) {
4347
let c = configuration.editor;
4448

4549
this.tabSize = modelOptions.tabSize;
50+
this.insertSpaces = modelOptions.insertSpaces;
51+
this.oneIndent = oneIndent;
4652
this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2;
4753
this.useTabStops = c.useTabStops;
4854
this.wordSeparators = c.wordSeparators;
4955
this.autoClosingBrackets = c.autoClosingBrackets;
5056
this.autoClosingPairsOpen = modeConfiguration.autoClosingPairsOpen;
5157
}
58+
59+
public normalizeIndentation(str: string): string {
60+
return TextModel.normalizeIndentation(str, this.tabSize, this.insertSpaces);
61+
}
5262
}
5363

5464
export interface ICursorMoveHelperModel {
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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 { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
8+
import { CursorMove, CursorMoveConfiguration, ICursorMoveHelperModel } from 'vs/editor/common/controller/cursorMoveHelper';
9+
import { Range } from 'vs/editor/common/core/range';
10+
import { CursorChangeReason } from 'vs/editor/common/editorCommon';
11+
import { CursorModelState } from 'vs/editor/common/controller/oneCursor';
12+
import * as strings from 'vs/base/common/strings';
13+
import { EditOperationResult } from 'vs/editor/common/controller/cursorDeleteOperations';
14+
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
15+
import { Selection } from 'vs/editor/common/core/selection';
16+
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
17+
import { ITokenizedModel } from 'vs/editor/common/editorCommon';
18+
import { IndentAction } from 'vs/editor/common/modes/languageConfiguration';
19+
20+
export class TypeOperations {
21+
22+
public static indent(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
23+
return new EditOperationResult(
24+
new ShiftCommand(cursor.selection, {
25+
isUnshift: false,
26+
tabSize: config.tabSize,
27+
oneIndent: config.oneIndent
28+
}), {
29+
shouldPushStackElementBefore: true,
30+
shouldPushStackElementAfter: true,
31+
shouldRevealHorizontal: false
32+
}
33+
);
34+
}
35+
36+
public static outdent(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState): EditOperationResult {
37+
return new EditOperationResult(
38+
new ShiftCommand(cursor.selection, {
39+
isUnshift: true,
40+
tabSize: config.tabSize,
41+
oneIndent: config.oneIndent
42+
}), {
43+
shouldPushStackElementBefore: true,
44+
shouldPushStackElementAfter: true,
45+
shouldRevealHorizontal: false
46+
}
47+
);
48+
}
49+
50+
public static paste(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, cursor: CursorModelState, text: string, pasteOnNewLine: boolean): EditOperationResult {
51+
let position = cursor.position;
52+
let selection = cursor.selection;
53+
54+
if (pasteOnNewLine && text.indexOf('\n') !== text.length - 1) {
55+
pasteOnNewLine = false;
56+
}
57+
if (pasteOnNewLine && selection.startLineNumber !== selection.endLineNumber) {
58+
pasteOnNewLine = false;
59+
}
60+
if (pasteOnNewLine && selection.startColumn === model.getLineMinColumn(selection.startLineNumber) && selection.endColumn === model.getLineMaxColumn(selection.startLineNumber)) {
61+
pasteOnNewLine = false;
62+
}
63+
64+
if (pasteOnNewLine) {
65+
// Paste entire line at the beginning of line
66+
67+
let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1);
68+
return new EditOperationResult(new ReplaceCommand(typeSelection, text), {
69+
shouldPushStackElementBefore: true,
70+
shouldPushStackElementAfter: true,
71+
cursorPositionChangeReason: CursorChangeReason.Paste
72+
});
73+
}
74+
75+
return new EditOperationResult(new ReplaceCommand(selection, text), {
76+
shouldPushStackElementBefore: true,
77+
shouldPushStackElementAfter: true,
78+
cursorPositionChangeReason: CursorChangeReason.Paste
79+
});
80+
}
81+
82+
private static _goodIndentForLine(config: CursorMoveConfiguration, model: ITokenizedModel, lineNumber: number): string {
83+
let lastLineNumber = lineNumber - 1;
84+
85+
for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) {
86+
let lineText = model.getLineContent(lastLineNumber);
87+
let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText);
88+
if (nonWhitespaceIdx >= 0) {
89+
break;
90+
}
91+
}
92+
93+
if (lastLineNumber < 1) {
94+
// No previous line with content found
95+
return '\t';
96+
}
97+
98+
let r = LanguageConfigurationRegistry.getEnterActionAtPosition(model, lastLineNumber, model.getLineMaxColumn(lastLineNumber));
99+
100+
let indentation: string;
101+
if (r.enterAction.indentAction === IndentAction.Outdent) {
102+
let desiredIndentCount = ShiftCommand.unshiftIndentCount(r.indentation, r.indentation.length, config.tabSize);
103+
indentation = '';
104+
for (let i = 0; i < desiredIndentCount; i++) {
105+
indentation += '\t';
106+
}
107+
indentation = config.normalizeIndentation(indentation);
108+
} else {
109+
indentation = r.indentation;
110+
}
111+
112+
let result = indentation + r.enterAction.appendText;
113+
if (result.length === 0) {
114+
// good position is at column 1, but we gotta do something...
115+
return '\t';
116+
}
117+
return result;
118+
}
119+
120+
private static _replaceJumpToNextIndent(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, selection: Selection): ReplaceCommand {
121+
let typeText = '';
122+
123+
let position = selection.getStartPosition();
124+
if (config.insertSpaces) {
125+
let visibleColumnFromColumn = CursorMove.visibleColumnFromColumn2(config, model, position);
126+
let tabSize = config.tabSize;
127+
let spacesCnt = tabSize - (visibleColumnFromColumn % tabSize);
128+
for (let i = 0; i < spacesCnt; i++) {
129+
typeText += ' ';
130+
}
131+
} else {
132+
typeText = '\t';
133+
}
134+
135+
return new ReplaceCommand(selection, typeText);
136+
}
137+
138+
public static tab(config: CursorMoveConfiguration, model: ITokenizedModel, cursor: CursorModelState): EditOperationResult {
139+
let selection = cursor.selection;
140+
141+
if (selection.isEmpty()) {
142+
143+
let lineText = model.getLineContent(selection.startLineNumber);
144+
145+
if (/^\s*$/.test(lineText)) {
146+
let possibleTypeText = config.normalizeIndentation(this._goodIndentForLine(config, model, selection.startLineNumber));
147+
if (!strings.startsWith(lineText, possibleTypeText)) {
148+
let command = new ReplaceCommand(new Range(selection.startLineNumber, 1, selection.startLineNumber, lineText.length + 1), possibleTypeText);
149+
return new EditOperationResult(command, {
150+
shouldPushStackElementBefore: false,
151+
shouldPushStackElementAfter: false,
152+
isAutoWhitespaceCommand: true
153+
});
154+
}
155+
}
156+
157+
return new EditOperationResult(this._replaceJumpToNextIndent(config, model, selection), {
158+
shouldPushStackElementBefore: false,
159+
shouldPushStackElementAfter: false,
160+
isAutoWhitespaceCommand: true
161+
});
162+
} else {
163+
if (selection.startLineNumber === selection.endLineNumber) {
164+
let lineMaxColumn = model.getLineMaxColumn(selection.startLineNumber);
165+
if (selection.startColumn !== 1 || selection.endColumn !== lineMaxColumn) {
166+
// This is a single line selection that is not the entire line
167+
return new EditOperationResult(this._replaceJumpToNextIndent(config, model, selection));
168+
}
169+
}
170+
171+
return this.indent(config, model, cursor);
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)