Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts
3296 views
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
6
import { CharCode } from '../../../../base/common/charCode.js';
7
import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js';
8
import { Position } from '../../../common/core/position.js';
9
import { Range } from '../../../common/core/range.js';
10
import { Selection } from '../../../common/core/selection.js';
11
import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js';
12
import { ITextModel } from '../../../common/model.js';
13
import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
14
15
export class BlockCommentCommand implements ICommand {
16
17
private readonly _selection: Selection;
18
private readonly _insertSpace: boolean;
19
private _usedEndToken: string | null;
20
21
constructor(
22
selection: Selection,
23
insertSpace: boolean,
24
private readonly languageConfigurationService: ILanguageConfigurationService
25
) {
26
this._selection = selection;
27
this._insertSpace = insertSpace;
28
this._usedEndToken = null;
29
}
30
31
public static _haystackHasNeedleAtOffset(haystack: string, needle: string, offset: number): boolean {
32
if (offset < 0) {
33
return false;
34
}
35
const needleLength = needle.length;
36
const haystackLength = haystack.length;
37
if (offset + needleLength > haystackLength) {
38
return false;
39
}
40
41
for (let i = 0; i < needleLength; i++) {
42
const codeA = haystack.charCodeAt(offset + i);
43
const codeB = needle.charCodeAt(i);
44
45
if (codeA === codeB) {
46
continue;
47
}
48
if (codeA >= CharCode.A && codeA <= CharCode.Z && codeA + 32 === codeB) {
49
// codeA is upper-case variant of codeB
50
continue;
51
}
52
if (codeB >= CharCode.A && codeB <= CharCode.Z && codeB + 32 === codeA) {
53
// codeB is upper-case variant of codeA
54
continue;
55
}
56
57
return false;
58
}
59
return true;
60
}
61
62
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, insertSpace: boolean, model: ITextModel, builder: IEditOperationBuilder): void {
63
const startLineNumber = selection.startLineNumber;
64
const startColumn = selection.startColumn;
65
const endLineNumber = selection.endLineNumber;
66
const endColumn = selection.endColumn;
67
68
const startLineText = model.getLineContent(startLineNumber);
69
const endLineText = model.getLineContent(endLineNumber);
70
71
let startTokenIndex = startLineText.lastIndexOf(startToken, startColumn - 1 + startToken.length);
72
let endTokenIndex = endLineText.indexOf(endToken, endColumn - 1 - endToken.length);
73
74
if (startTokenIndex !== -1 && endTokenIndex !== -1) {
75
76
if (startLineNumber === endLineNumber) {
77
const lineBetweenTokens = startLineText.substring(startTokenIndex + startToken.length, endTokenIndex);
78
79
if (lineBetweenTokens.indexOf(endToken) >= 0) {
80
// force to add a block comment
81
startTokenIndex = -1;
82
endTokenIndex = -1;
83
}
84
} else {
85
const startLineAfterStartToken = startLineText.substring(startTokenIndex + startToken.length);
86
const endLineBeforeEndToken = endLineText.substring(0, endTokenIndex);
87
88
if (startLineAfterStartToken.indexOf(endToken) >= 0 || endLineBeforeEndToken.indexOf(endToken) >= 0) {
89
// force to add a block comment
90
startTokenIndex = -1;
91
endTokenIndex = -1;
92
}
93
}
94
}
95
96
let ops: ISingleEditOperation[];
97
98
if (startTokenIndex !== -1 && endTokenIndex !== -1) {
99
// Consider spaces as part of the comment tokens
100
if (insertSpace && startTokenIndex + startToken.length < startLineText.length && startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) {
101
// Pretend the start token contains a trailing space
102
startToken = startToken + ' ';
103
}
104
105
if (insertSpace && endTokenIndex > 0 && endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) {
106
// Pretend the end token contains a leading space
107
endToken = ' ' + endToken;
108
endTokenIndex -= 1;
109
}
110
ops = BlockCommentCommand._createRemoveBlockCommentOperations(
111
new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken
112
);
113
} else {
114
ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken, this._insertSpace);
115
this._usedEndToken = ops.length === 1 ? endToken : null;
116
}
117
118
for (const op of ops) {
119
builder.addTrackedEditOperation(op.range, op.text);
120
}
121
}
122
123
public static _createRemoveBlockCommentOperations(r: Range, startToken: string, endToken: string): ISingleEditOperation[] {
124
const res: ISingleEditOperation[] = [];
125
126
if (!Range.isEmpty(r)) {
127
// Remove block comment start
128
res.push(EditOperation.delete(new Range(
129
r.startLineNumber, r.startColumn - startToken.length,
130
r.startLineNumber, r.startColumn
131
)));
132
133
// Remove block comment end
134
res.push(EditOperation.delete(new Range(
135
r.endLineNumber, r.endColumn,
136
r.endLineNumber, r.endColumn + endToken.length
137
)));
138
} else {
139
// Remove both continuously
140
res.push(EditOperation.delete(new Range(
141
r.startLineNumber, r.startColumn - startToken.length,
142
r.endLineNumber, r.endColumn + endToken.length
143
)));
144
}
145
146
return res;
147
}
148
149
public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string, insertSpace: boolean): ISingleEditOperation[] {
150
const res: ISingleEditOperation[] = [];
151
152
if (!Range.isEmpty(r)) {
153
// Insert block comment start
154
res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + (insertSpace ? ' ' : '')));
155
156
// Insert block comment end
157
res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), (insertSpace ? ' ' : '') + endToken));
158
} else {
159
// Insert both continuously
160
res.push(EditOperation.replace(new Range(
161
r.startLineNumber, r.startColumn,
162
r.endLineNumber, r.endColumn
163
), startToken + ' ' + endToken));
164
}
165
166
return res;
167
}
168
169
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
170
const startLineNumber = this._selection.startLineNumber;
171
const startColumn = this._selection.startColumn;
172
173
model.tokenization.tokenizeIfCheap(startLineNumber);
174
const languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn);
175
const config = this.languageConfigurationService.getLanguageConfiguration(languageId).comments;
176
if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) {
177
// Mode does not support block comments
178
return;
179
}
180
181
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder);
182
}
183
184
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
185
const inverseEditOperations = helper.getInverseEditOperations();
186
if (inverseEditOperations.length === 2) {
187
const startTokenEditOperation = inverseEditOperations[0];
188
const endTokenEditOperation = inverseEditOperations[1];
189
190
return new Selection(
191
startTokenEditOperation.range.endLineNumber,
192
startTokenEditOperation.range.endColumn,
193
endTokenEditOperation.range.startLineNumber,
194
endTokenEditOperation.range.startColumn
195
);
196
} else {
197
const srcRange = inverseEditOperations[0].range;
198
const deltaColumn = this._usedEndToken ? -this._usedEndToken.length - 1 : 0; // minus 1 space before endToken
199
return new Selection(
200
srcRange.endLineNumber,
201
srcRange.endColumn + deltaColumn,
202
srcRange.endLineNumber,
203
srcRange.endColumn + deltaColumn
204
);
205
}
206
}
207
}
208
209