Path: blob/main/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { DisposableStore } from '../../../../base/common/lifecycle.js';7import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';8import { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from '../../../common/commands/trimTrailingWhitespaceCommand.js';9import { ISingleEditOperation } from '../../../common/core/editOperation.js';10import { Position } from '../../../common/core/position.js';11import { Range } from '../../../common/core/range.js';12import { Selection } from '../../../common/core/selection.js';13import { MetadataConsts, StandardTokenType } from '../../../common/encodedTokenAttributes.js';14import { EncodedTokenizationResult, ITokenizationSupport, TokenizationRegistry } from '../../../common/languages.js';15import { ILanguageService } from '../../../common/languages/language.js';16import { NullState } from '../../../common/languages/nullTokenize.js';17import { getEditOperation } from '../testCommand.js';18import { createModelServices, instantiateTextModel, withEditorModel } from '../../common/testTextModel.js';1920/**21* Create single edit operation22*/23function createInsertDeleteSingleEditOp(text: string | null, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): ISingleEditOperation {24return {25range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),26text: text27};28}2930/**31* Create single edit operation32*/33function createSingleEditOp(text: string | null, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): ISingleEditOperation {34return {35range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn),36text: text,37forceMoveMarkers: false38};39}4041function assertTrimTrailingWhitespaceCommand(text: string[], expected: ISingleEditOperation[]): void {42return withEditorModel(text, (model) => {43const op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1), [], true);44const actual = getEditOperation(model, op);45assert.deepStrictEqual(actual, expected);46});47}4849function assertTrimTrailingWhitespace(text: string[], cursors: Position[], expected: ISingleEditOperation[]): void {50return withEditorModel(text, (model) => {51const actual = trimTrailingWhitespace(model, cursors, true);52assert.deepStrictEqual(actual, expected);53});54}5556suite('Editor Commands - Trim Trailing Whitespace Command', () => {5758let disposables: DisposableStore;5960setup(() => {61disposables = new DisposableStore();62});6364teardown(() => {65disposables.dispose();66});6768ensureNoDisposablesAreLeakedInTestSuite();6970test('remove trailing whitespace', function () {71assertTrimTrailingWhitespaceCommand([''], []);72assertTrimTrailingWhitespaceCommand(['text'], []);73assertTrimTrailingWhitespaceCommand(['text '], [createSingleEditOp(null, 1, 5, 1, 8)]);74assertTrimTrailingWhitespaceCommand(['text\t '], [createSingleEditOp(null, 1, 5, 1, 9)]);75assertTrimTrailingWhitespaceCommand(['\t '], [createSingleEditOp(null, 1, 1, 1, 5)]);76assertTrimTrailingWhitespaceCommand(['text\t'], [createSingleEditOp(null, 1, 5, 1, 6)]);77assertTrimTrailingWhitespaceCommand([78'some text\t',79'some more text',80'\t ',81'even more text ',82'and some mixed\t \t'83], [84createSingleEditOp(null, 1, 10, 1, 11),85createSingleEditOp(null, 3, 1, 3, 4),86createSingleEditOp(null, 4, 15, 4, 17),87createSingleEditOp(null, 5, 15, 5, 20)88]);899091assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 2), new Position(1, 3)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);92assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5)], [createInsertDeleteSingleEditOp(null, 1, 5, 1, 8)]);93assertTrimTrailingWhitespace(['text '], [new Position(1, 1), new Position(1, 5), new Position(1, 6)], [createInsertDeleteSingleEditOp(null, 1, 6, 1, 8)]);94assertTrimTrailingWhitespace([95'some text\t',96'some more text',97'\t ',98'even more text ',99'and some mixed\t \t'100], [], [101createInsertDeleteSingleEditOp(null, 1, 10, 1, 11),102createInsertDeleteSingleEditOp(null, 3, 1, 3, 4),103createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),104createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)105]);106assertTrimTrailingWhitespace([107'some text\t',108'some more text',109'\t ',110'even more text ',111'and some mixed\t \t'112], [new Position(1, 11), new Position(3, 2), new Position(5, 1), new Position(4, 1), new Position(5, 10)], [113createInsertDeleteSingleEditOp(null, 3, 2, 3, 4),114createInsertDeleteSingleEditOp(null, 4, 15, 4, 17),115createInsertDeleteSingleEditOp(null, 5, 15, 5, 20)116]);117});118119test('skips strings and regex if configured', function () {120const instantiationService = createModelServices(disposables);121const languageService = instantiationService.get(ILanguageService);122const languageId = 'testLanguageId';123const languageIdCodec = languageService.languageIdCodec;124disposables.add(languageService.registerLanguage({ id: languageId }));125const encodedLanguageId = languageIdCodec.encodeLanguageId(languageId);126127const otherMetadata = (128(encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET)129| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)130| (MetadataConsts.BALANCED_BRACKETS_MASK)131) >>> 0;132const stringMetadata = (133(encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET)134| (StandardTokenType.String << MetadataConsts.TOKEN_TYPE_OFFSET)135| (MetadataConsts.BALANCED_BRACKETS_MASK)136) >>> 0;137138const tokenizationSupport: ITokenizationSupport = {139getInitialState: () => NullState,140tokenize: undefined!,141tokenizeEncoded: (line, hasEOL, state) => {142switch (line) {143case 'const a = ` ': {144const tokens = new Uint32Array([1450, otherMetadata,14610, stringMetadata,147]);148return new EncodedTokenizationResult(tokens, state);149}150case ' a string ': {151const tokens = new Uint32Array([1520, stringMetadata,153]);154return new EncodedTokenizationResult(tokens, state);155}156case '`; ': {157const tokens = new Uint32Array([1580, stringMetadata,1591, otherMetadata160]);161return new EncodedTokenizationResult(tokens, state);162}163}164throw new Error(`Unexpected`);165}166};167168disposables.add(TokenizationRegistry.register(languageId, tokenizationSupport));169170const model = disposables.add(instantiateTextModel(171instantiationService,172[173'const a = ` ',174' a string ',175'`; ',176].join('\n'),177languageId178));179180model.tokenization.forceTokenization(1);181model.tokenization.forceTokenization(2);182model.tokenization.forceTokenization(3);183184const op = new TrimTrailingWhitespaceCommand(new Selection(1, 1, 1, 1), [], false);185const actual = getEditOperation(model, op);186assert.deepStrictEqual(actual, [createSingleEditOp(null, 3, 3, 3, 5)]);187});188});189190191