diff --git a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts index 92423c46a70..7b26a3af664 100644 --- a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts +++ b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts @@ -732,6 +732,8 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { // issue: Should indent after an equal sign is detected followed by whitespace characters. // This should be outdented when a semi-colon is detected indicating the end of the assignment. + // TODO: requires exploring indent/outdent pairs instead + const model = createTextModel([ 'const array =' ].join('\n'), languageId, {}); @@ -753,6 +755,8 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { // https://github.com/microsoft/vscode/issues/43244 // issue: When a dot is written, we should detect that this is a method call and indent accordingly + // TODO: requires exploring indent/outdent pairs instead + const model = createTextModel([ 'const array = [1, 2, 3];', 'array.' @@ -776,6 +780,8 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { // https://github.com/microsoft/vscode/issues/43244 // issue: When a dot is written, we should detect that this is a method call and indent accordingly + // TODO: requires exploring indent/outdent pairs instead + const model = createTextModel([ 'const array = [1, 2, 3]', ].join('\n'), languageId, {}); @@ -798,6 +804,8 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { // https://github.com/microsoft/vscode/issues/43244 // Currently passes, but should pass with all the tests above too + // TODO: requires exploring indent/outdent pairs instead + const model = createTextModel([ 'const array = [1, 2, 3]', ' .filter(() => true)' @@ -816,10 +824,38 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { }); }); + test.skip('issue #43244: keep indentation when chained methods called on object/array', () => { + + // https://github.com/microsoft/vscode/issues/43244 + // When the call chain is not finished yet, and we type a dot, we do not want to change the indentation + + // TODO: requires exploring indent/outdent pairs instead + + const model = createTextModel([ + 'const array = [1, 2, 3]', + ' .filter(() => true)', + ' ' + ].join('\n'), languageId, {}); + disposables.add(model); + + withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => { + registerLanguage(instantiationService, languageId, Language.TypeScript, disposables); + editor.setSelection(new Selection(3, 5, 3, 5)); + viewModel.type("."); + assert.strictEqual(model.getValue(), [ + 'const array = [1, 2, 3]', + ' .filter(() => true)', + ' .' // here we don't want to increase the indentation because we have chained methods + ].join('\n')); + }); + }); + test.skip('issue #43244: outdent when a semi-color is detected indicating the end of the assignment', () => { // https://github.com/microsoft/vscode/issues/43244 + // TODO: requires exploring indent/outdent pairs instead + const model = createTextModel([ 'const array = [1, 2, 3]', ' .filter(() => true);' @@ -844,17 +880,17 @@ suite('`Full` Auto Indent On Type - TypeScript/JavaScript', () => { const model = createTextModel([ 'const array = [1, 2, 3, 4, 5];', - 'array.map(v =>)' + 'array.map(_)' ].join('\n'), languageId, {}); disposables.add(model); withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => { registerLanguage(instantiationService, languageId, Language.TypeScript, disposables); - editor.setSelection(new Selection(2, 15, 2, 15)); + editor.setSelection(new Selection(2, 12, 2, 12)); viewModel.type("\n", 'keyboard'); assert.strictEqual(model.getValue(), [ 'const array = [1, 2, 3, 4, 5];', - 'array.map(v =>', + 'array.map(_', ' ', ')' ].join('\n')); diff --git a/src/vs/editor/contrib/rename/browser/rename.ts b/src/vs/editor/contrib/rename/browser/rename.ts index dba920953ba..10786e237ec 100644 --- a/src/vs/editor/contrib/rename/browser/rename.ts +++ b/src/vs/editor/contrib/rename/browser/rename.ts @@ -38,7 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { CONTEXT_RENAME_INPUT_VISIBLE, NewNameSource, RenameInputFieldResult, RenameWidget } from './renameInputField'; +import { CONTEXT_RENAME_INPUT_VISIBLE, NewNameSource, RenameWidget, RenameWidgetResult } from './renameWidget'; class RenameSkeleton { @@ -138,7 +138,7 @@ class RenameController implements IEditorContribution { return editor.getContribution(RenameController.ID); } - private readonly _renameInputField: RenameWidget; + private readonly _renameWidget: RenameWidget; private readonly _disposableStore = new DisposableStore(); private _cts: CancellationTokenSource = new CancellationTokenSource(); @@ -153,7 +153,7 @@ class RenameController implements IEditorContribution { @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { - this._renameInputField = this._disposableStore.add(this._instaService.createInstance(RenameWidget, this.editor, ['acceptRenameInput', 'acceptRenameInputWithPreview'])); + this._renameWidget = this._disposableStore.add(this._instaService.createInstance(RenameWidget, this.editor, ['acceptRenameInput', 'acceptRenameInputWithPreview'])); } dispose(): void { @@ -235,7 +235,7 @@ class RenameController implements IEditorContribution { trace('creating rename input field and awaiting its result'); const supportPreview = this._bulkEditService.hasPreviewHandler() && this._configService.getValue(this.editor.getModel().uri, 'editor.rename.enablePreview'); - const inputFieldResult = await this._renameInputField.getInput( + const inputFieldResult = await this._renameWidget.getInput( loc.range, loc.text, supportPreview, @@ -321,22 +321,22 @@ class RenameController implements IEditorContribution { } acceptRenameInput(wantsPreview: boolean): void { - this._renameInputField.acceptInput(wantsPreview); + this._renameWidget.acceptInput(wantsPreview); } cancelRenameInput(): void { - this._renameInputField.cancelInput(true, 'cancelRenameInput command'); + this._renameWidget.cancelInput(true, 'cancelRenameInput command'); } focusNextRenameSuggestion(): void { - this._renameInputField.focusNextRenameSuggestion(); + this._renameWidget.focusNextRenameSuggestion(); } focusPreviousRenameSuggestion(): void { - this._renameInputField.focusPreviousRenameSuggestion(); + this._renameWidget.focusPreviousRenameSuggestion(); } - private _reportTelemetry(nRenameSuggestionProviders: number, languageId: string, inputFieldResult: boolean | RenameInputFieldResult, inDebugMode?: 'inDebugMode') { + private _reportTelemetry(nRenameSuggestionProviders: number, languageId: string, inputFieldResult: boolean | RenameWidgetResult, inDebugMode?: 'inDebugMode') { type RenameInvokedEvent = { kind: 'accepted' | 'cancelled'; @@ -367,22 +367,23 @@ class RenameController implements IEditorContribution { wantsPreview?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If user wanted preview.'; isMeasurement: true }; }; - const value: RenameInvokedEvent = typeof inputFieldResult === 'boolean' - ? { - kind: 'cancelled', - languageId, - nRenameSuggestionProviders, - } - : { - kind: 'accepted', - languageId, - nRenameSuggestionProviders, + const value: RenameInvokedEvent = + typeof inputFieldResult === 'boolean' + ? { + kind: 'cancelled', + languageId, + nRenameSuggestionProviders, + } + : { + kind: 'accepted', + languageId, + nRenameSuggestionProviders, - source: inputFieldResult.stats.source.k, - nRenameSuggestions: inputFieldResult.stats.nRenameSuggestions, - timeBeforeFirstInputFieldEdit: inputFieldResult.stats.timeBeforeFirstInputFieldEdit, - wantsPreview: inputFieldResult.wantsPreview, - }; + source: inputFieldResult.stats.source.k, + nRenameSuggestions: inputFieldResult.stats.nRenameSuggestions, + timeBeforeFirstInputFieldEdit: inputFieldResult.stats.timeBeforeFirstInputFieldEdit, + wantsPreview: inputFieldResult.wantsPreview, + }; if (inDebugMode) { this._telemetryService.publicLog2('renameInvokedEventDebug', value); diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.css b/src/vs/editor/contrib/rename/browser/renameWidget.css similarity index 100% rename from src/vs/editor/contrib/rename/browser/renameInputField.css rename to src/vs/editor/contrib/rename/browser/renameWidget.css diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts similarity index 96% rename from src/vs/editor/contrib/rename/browser/renameInputField.ts rename to src/vs/editor/contrib/rename/browser/renameWidget.ts index eb6bfde534b..ce051f73bb7 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -16,7 +16,7 @@ import { Emitter } from 'vs/base/common/event'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType, isDefined } from 'vs/base/common/types'; -import 'vs/css!./renameInputField'; +import 'vs/css!./renameWidget'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -66,24 +66,24 @@ export type NewNameSource = /** * Various statistics regarding rename input field */ -export type RenameInputFieldStats = { +export type RenameWidgetStats = { nRenameSuggestions: number; source: NewNameSource; timeBeforeFirstInputFieldEdit: number | undefined; }; -export type RenameInputFieldResult = { +export type RenameWidgetResult = { /** * The new name to be used */ newName: string; wantsPreview?: boolean; - stats: RenameInputFieldStats; + stats: RenameWidgetStats; }; -interface IRenameInputField { +interface IRenameWidget { /** - * @returns a `boolean` standing for `shouldFocusEditor`, if user didn't pick a new name, or a {@link RenameInputFieldResult} + * @returns a `boolean` standing for `shouldFocusEditor`, if user didn't pick a new name, or a {@link RenameWidgetResult} */ getInput( where: IRange, @@ -91,7 +91,7 @@ interface IRenameInputField { supportPreview: boolean, requestRenameSuggestions: (cts: CancellationToken) => ProviderResult[], cts: CancellationTokenSource - ): Promise; + ): Promise; acceptInput(wantsPreview: boolean): void; cancelInput(focusEditor: boolean, caller: string): void; @@ -100,7 +100,7 @@ interface IRenameInputField { focusPreviousRenameSuggestion(): void; } -export class RenameWidget implements IRenameInputField, IContentWidget, IDisposable { +export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable { // implement IContentWidget readonly allowEditorOverflow: boolean = true; @@ -243,7 +243,7 @@ export class RenameWidget implements IRenameInputField, IContentWidget, IDisposa if (this._domNode === undefined) { return; } - assertType(this._label !== undefined, 'RenameInputField#_updateFont: _label must not be undefined given _domNode is defined'); + assertType(this._label !== undefined, 'RenameWidget#_updateFont: _label must not be undefined given _domNode is defined'); this._editor.applyFontInfo(this._input.domNode); @@ -361,14 +361,14 @@ export class RenameWidget implements IRenameInputField, IContentWidget, IDisposa where: IRange, currentName: string, supportPreview: boolean, - requestRenameSuggestions: (cts: CancellationToken) => ProviderResult[], + requestRenameCandidates: (cts: CancellationToken) => ProviderResult[], cts: CancellationTokenSource - ): Promise { + ): Promise { const { start: selectionStart, end: selectionEnd } = this._getSelection(where, currentName); this._renameCandidateProvidersCts = new CancellationTokenSource(); - const candidates = requestRenameSuggestions(this._renameCandidateProvidersCts.token); + const candidates = requestRenameCandidates(this._renameCandidateProvidersCts.token); this._updateRenameCandidates(candidates, currentName, cts.token); this._isEditingRenameCandidate = false; @@ -395,7 +395,7 @@ export class RenameWidget implements IRenameInputField, IContentWidget, IDisposa } })); - const inputResult = new DeferredPromise(); + const inputResult = new DeferredPromise(); inputResult.p.finally(() => { disposeOnDone.dispose(); @@ -547,7 +547,7 @@ export class RenameWidget implements IRenameInputField, IContentWidget, IDisposa if (visibleRanges.length > 0) { firstLineInViewport = visibleRanges[0].startLineNumber; } else { - this._logService.warn('RenameInputField#_getTopForPosition: this should not happen - visibleRanges is empty'); + this._logService.warn('RenameWidget#_getTopForPosition: this should not happen - visibleRanges is empty'); firstLineInViewport = Math.max(1, this._position!.lineNumber - 5); // @ulugbekna: fallback to current line minus 5 } return this._editor.getTopForLineNumber(this._position!.lineNumber) - this._editor.getTopForLineNumber(firstLineInViewport); diff --git a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts index 0378af3ac75..6d58f57ebba 100644 --- a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts @@ -190,7 +190,7 @@ suite('Auto-Reindentation - TypeScript/JavaScript', () => { // explanation: if (, [, {, is followed by a forward slash then assume we are in a regex pattern, and do not indent // increaseIndentPattern: /^((?!\/\/).)*(\{([^}"'`]*|(\t|[ ])*\/\/.*)|\([^)"'`]*|\[[^\]"'`]*)$/ -> /^((?!\/\/).)*(\{([^}"'`/]*|(\t|[ ])*\/\/.*)|\([^)"'`/]*|\[[^\]"'`/]*)$/ - // -> Final current increase indent pattern + // -> Final current increase indent pattern at of writing const fileContents = [ 'const r = /{/;', @@ -214,7 +214,7 @@ suite('Auto-Reindentation - TypeScript/JavaScript', () => { // fix: https://github.com/microsoft/vscode/commit/7910b3d7bab8a721aae98dc05af0b5e1ea9d9782 // decreaseIndentPattern: /^(.*\*\/)?\s*[\}\]\)].*$/ -> /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/ - // -> Final current decrease indent pattern + // -> Final current decrease indent pattern at the time of writing // explanation: Positive lookahead: (?= «pattern») matches if pattern matches what comes after the current location in the input string. // Negative lookahead: (?! «pattern») matches if pattern does not match what comes after the current location in the input string @@ -282,10 +282,8 @@ suite('Auto-Reindentation - TypeScript/JavaScript', () => { // related: https://github.com/microsoft/vscode/issues/43244 // explanation: When you have an arrow function, you don't have { or }, but you would expect indentation to still be done in that way - /* - Notes: Currently the reindent edit operations does not call the onEnter rules - The reindent should also call the onEnter rules to get the correct indentation - */ + // TODO: requires exploring indent/outdent pairs instead + const fileContents = [ 'const add1 = (n) =>', ' n + 1;',