Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.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 assert from 'assert';
7
import { DisposableStore } from '../../../../base/common/lifecycle.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from '../../../common/commands/trimTrailingWhitespaceCommand.js';
10
import { ISingleEditOperation } from '../../../common/core/editOperation.js';
11
import { Position } from '../../../common/core/position.js';
12
import { Range } from '../../../common/core/range.js';
13
import { Selection } from '../../../common/core/selection.js';
14
import { MetadataConsts, StandardTokenType } from '../../../common/encodedTokenAttributes.js';
15
import { EncodedTokenizationResult, ITokenizationSupport, TokenizationRegistry } from '../../../common/languages.js';
16
import { ILanguageService } from '../../../common/languages/language.js';
17
import { NullState } from '../../../common/languages/nullTokenize.js';
18
import { getEditOperation } from '../testCommand.js';
19
import { createModelServices, instantiateTextModel, withEditorModel } from '../../common/testTextModel.js';
20
21
/**
22
* Create single edit operation
23
*/
24
function createInsertDeleteSingleEditOp(text: string | null, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): ISingleEditOperation {
25
return {
26
range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),
27
text: text
28
};
29
}
30
31
/**
32
* Create single edit operation
33
*/
34
function createSingleEditOp(text: string | null, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): ISingleEditOperation {
35
return {
36
range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),
37
text: text,
38
forceMoveMarkers: false
39
};
40
}
41
42
function assertTrimTrailingWhitespaceCommand(text: string[], expected: ISingleEditOperation[]): void {
43
return withEditorModel(text, (model) => {
44
const op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1), [], true);
45
const actual = getEditOperation(model, op);
46
assert.deepStrictEqual(actual, expected);
47
});
48
}
49
50
function assertTrimTrailingWhitespace(text: string[], cursors: Position[], expected: ISingleEditOperation[]): void {
51
return withEditorModel(text, (model) => {
52
const actual = trimTrailingWhitespace(model, cursors, true);
53
assert.deepStrictEqual(actual, expected);
54
});
55
}
56
57
suite('Editor Commands - Trim Trailing Whitespace Command', () => {
58
59
let disposables: DisposableStore;
60
61
setup(() => {
62
disposables = new DisposableStore();
63
});
64
65
teardown(() => {
66
disposables.dispose();
67
});
68
69
ensureNoDisposablesAreLeakedInTestSuite();
70
71
test('remove trailing whitespace', function () {
72
assertTrimTrailingWhitespaceCommand([''], []);
73
assertTrimTrailingWhitespaceCommand(['text'], []);
74
assertTrimTrailingWhitespaceCommand(['text '], [createSingleEditOp(null, 1, 5, 1, 8)]);
75
assertTrimTrailingWhitespaceCommand(['text\t '], [createSingleEditOp(null, 1, 5, 1, 9)]);
76
assertTrimTrailingWhitespaceCommand(['\t '], [createSingleEditOp(null, 1, 1, 1, 5)]);
77
assertTrimTrailingWhitespaceCommand(['text\t'], [createSingleEditOp(null, 1, 5, 1, 6)]);
78
assertTrimTrailingWhitespaceCommand([
79
'some text\t',
80
'some more text',
81
'\t ',
82
'even more text ',
83
'and some mixed\t \t'
84
], [
85
createSingleEditOp(null, 1, 10, 1, 11),
86
createSingleEditOp(null, 3, 1, 3, 4),
87
createSingleEditOp(null, 4, 15, 4, 17),
88
createSingleEditOp(null, 5, 15, 5, 20)
89
]);
90
91
92
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 2), new Position(1, 3)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);
93
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);
94
assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5), new Position(1, 6)], [createInsertDeleteSingleEditOp(null, 1, 6, 1, 8)]);
95
assertTrimTrailingWhitespace([
96
'some text\t',
97
'some more text',
98
'\t ',
99
'even more text ',
100
'and some mixed\t \t'
101
], [], [
102
createInsertDeleteSingleEditOp(null, 1, 10, 1, 11),
103
createInsertDeleteSingleEditOp(null, 3, 1, 3, 4),
104
createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),
105
createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)
106
]);
107
assertTrimTrailingWhitespace([
108
'some text\t',
109
'some more text',
110
'\t ',
111
'even more text ',
112
'and some mixed\t \t'
113
], [new Position(1, 11), new Position(3, 2), new Position(5, 1), new Position(4, 1), new Position(5, 10)], [
114
createInsertDeleteSingleEditOp(null, 3, 2, 3, 4),
115
createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),
116
createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)
117
]);
118
});
119
120
test('skips strings and regex if configured', function () {
121
const instantiationService = createModelServices(disposables);
122
const languageService = instantiationService.get(ILanguageService);
123
const languageId = 'testLanguageId';
124
const languageIdCodec = languageService.languageIdCodec;
125
disposables.add(languageService.registerLanguage({ id: languageId }));
126
const encodedLanguageId = languageIdCodec.encodeLanguageId(languageId);
127
128
const otherMetadata = (
129
(encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET)
130
| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)
131
| (MetadataConsts.BALANCED_BRACKETS_MASK)
132
) >>> 0;
133
const stringMetadata = (
134
(encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET)
135
| (StandardTokenType.String << MetadataConsts.TOKEN_TYPE_OFFSET)
136
| (MetadataConsts.BALANCED_BRACKETS_MASK)
137
) >>> 0;
138
139
const tokenizationSupport: ITokenizationSupport = {
140
getInitialState: () => NullState,
141
tokenize: undefined!,
142
tokenizeEncoded: (line, hasEOL, state) => {
143
switch (line) {
144
case 'const a = ` ': {
145
const tokens = new Uint32Array([
146
0, otherMetadata,
147
10, stringMetadata,
148
]);
149
return new EncodedTokenizationResult(tokens, state);
150
}
151
case ' a string ': {
152
const tokens = new Uint32Array([
153
0, stringMetadata,
154
]);
155
return new EncodedTokenizationResult(tokens, state);
156
}
157
case '`; ': {
158
const tokens = new Uint32Array([
159
0, stringMetadata,
160
1, otherMetadata
161
]);
162
return new EncodedTokenizationResult(tokens, state);
163
}
164
}
165
throw new Error(`Unexpected`);
166
}
167
};
168
169
disposables.add(TokenizationRegistry.register(languageId, tokenizationSupport));
170
171
const model = disposables.add(instantiateTextModel(
172
instantiationService,
173
[
174
'const a = ` ',
175
' a string ',
176
'`; ',
177
].join('\n'),
178
languageId
179
));
180
181
model.tokenization.forceTokenization(1);
182
model.tokenization.forceTokenization(2);
183
model.tokenization.forceTokenization(3);
184
185
const op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1), [], false);
186
const actual = getEditOperation(model, op);
187
assert.deepStrictEqual(actual, [createSingleEditOp(null, 3, 3, 3, 5)]);
188
});
189
});
190
191