From eedf704b0ba387a191bd80faefc37ce0493fe7c3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 28 Oct 2020 18:14:01 +0100 Subject: [PATCH] add config option to disable whitespace select --- .../editor/contrib/smartSelect/smartSelect.ts | 40 +++++++++++++++++-- .../smartSelect/test/smartSelect.test.ts | 26 +++++++++++- .../api/extHostLanguageFeatures.test.ts | 2 +- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index b6de17c9c99..714a27d7e48 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -23,6 +23,9 @@ import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSe import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; class SelectionRanges { @@ -59,7 +62,10 @@ class SmartSelectController implements IEditorContribution { private _selectionListener?: IDisposable; private _ignoreSelection: boolean = false; - constructor(editor: ICodeEditor) { + constructor( + editor: ICodeEditor, + @ITextResourceConfigurationService private readonly _configService: ITextResourceConfigurationService, + ) { this._editor = editor; } @@ -83,7 +89,10 @@ class SmartSelectController implements IEditorContribution { let promise: Promise = Promise.resolve(undefined); if (!this._state) { - promise = provideSelectionRanges(model, selections.map(s => s.getPosition()), CancellationToken.None).then(ranges => { + + const selectLeadingAndTrailingWhitespace = this._configService.getValue(model.uri, 'editor.smartSelect.selectLeadingAndTrailingWhitespace') ?? true; + + promise = provideSelectionRanges(model, selections.map(s => s.getPosition()), { selectLeadingAndTrailingWhitespace }, CancellationToken.None).then(ranges => { if (!arrays.isNonEmptyArray(ranges) || ranges.length !== selections.length) { // invalid result return; @@ -177,6 +186,21 @@ class GrowSelectionAction extends AbstractSmartSelect { } } +//todo@jrieken use proper editor config instead. however, to keep the number +// of changes low use the quick config definition +Registry.as(Extensions.Configuration).registerConfiguration({ + id: 'editor', + properties: { + 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + description: nls.localize('selectLeadingAndTrailingWhitespace', "Weather leading and trailing whitespace should always be selected."), + default: true, + type: 'boolean' + } + } +}); + + // renamed command id CommandsRegistry.registerCommandAlias('editor.action.smartSelect.grow', 'editor.action.smartSelect.expand'); @@ -213,7 +237,11 @@ registerEditorAction(ShrinkSelectionAction); // word selection modes.SelectionRangeRegistry.register('*', new WordSelectionRangeProvider()); -export function provideSelectionRanges(model: ITextModel, positions: Position[], token: CancellationToken): Promise { +export interface SelectionRangesOptions { + selectLeadingAndTrailingWhitespace: boolean +} + +export function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { const providers = modes.SelectionRangeRegistry.all(model); @@ -277,6 +305,10 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], } } + if (!options.selectLeadingAndTrailingWhitespace) { + return oneRanges; + } + // add ranges that expand trivia at line starts and ends whenever a range // wraps onto the a new line let oneRangesWithTrivia: Range[] = [oneRanges[0]]; @@ -304,5 +336,5 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], registerModelCommand('_executeSelectionRangeProvider', function (model, ...args) { const [positions] = args; - return provideSelectionRanges(model, positions, CancellationToken.None); + return provideSelectionRanges(model, positions, { selectLeadingAndTrailingWhitespace: true }, CancellationToken.None); }); diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 5ca1c5b5927..4c566492b68 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -60,10 +60,10 @@ suite('SmartSelect', () => { mode.dispose(); }); - async function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[]): Promise { + async function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[], selectLeadingAndTrailingWhitespace = true): Promise { let uri = URI.file('test.js'); let model = modelService.createModel(text.join('\n'), new StaticLanguageSelector(mode.getLanguageIdentifier()), uri); - let [actual] = await provideSelectionRanges(model, [new Position(lineNumber, column)], CancellationToken.None); + let [actual] = await provideSelectionRanges(model, [new Position(lineNumber, column)], { selectLeadingAndTrailingWhitespace }, CancellationToken.None); let actualStr = actual!.map(r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn).toString()); let desiredStr = ranges.reverse().map(r => String(r)); @@ -97,6 +97,28 @@ suite('SmartSelect', () => { ]); }); + test('config: selectLeadingAndTrailingWhitespace', async () => { + + await assertGetRangesToPosition([ + 'aaa', + '\tbbb', + '' + ], 2, 3, [ + new Range(1, 1, 3, 1), // all + new Range(2, 1, 2, 5), // line w/ triva + new Range(2, 2, 2, 5), // bbb + ], true); + + await assertGetRangesToPosition([ + 'aaa', + '\tbbb', + '' + ], 2, 3, [ + new Range(1, 1, 3, 1), // all + new Range(2, 2, 2, 5), // () inside + ], false); + }); + test('getRangesToPosition #56886. Skip empty lines correctly.', () => { return assertGetRangesToPosition([ diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index efc8b65b150..82e27a2ea17 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -1170,7 +1170,7 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); - provideSelectionRanges(model, [new Position(1, 17)], CancellationToken.None).then(ranges => { + provideSelectionRanges(model, [new Position(1, 17)], { selectLeadingAndTrailingWhitespace: true }, CancellationToken.None).then(ranges => { assert.equal(ranges.length, 1); assert.ok(ranges[0].length >= 2); }); -- GitLab