From 53a40e7bf30b24f32aa41024affca3ae8dcbfe88 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 13 Mar 2023 22:15:47 +0300 Subject: [PATCH 1/7] Use default settings when format options not passed in --- .../src/configs/getFormatCodeSettings.ts | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/typescript/src/configs/getFormatCodeSettings.ts b/packages/typescript/src/configs/getFormatCodeSettings.ts index 5ed12bd4..8284ea93 100644 --- a/packages/typescript/src/configs/getFormatCodeSettings.ts +++ b/packages/typescript/src/configs/getFormatCodeSettings.ts @@ -14,28 +14,36 @@ export async function getFormatCodeSettings( config = config ?? {}; - return { - convertTabsToSpaces: options?.insertSpaces, - tabSize: options?.tabSize, - indentSize: options?.tabSize, - indentStyle: 2 /** ts.IndentStyle.Smart */, - newLineCharacter: '\n', - insertSpaceAfterCommaDelimiter: config.insertSpaceAfterCommaDelimiter ?? true, - insertSpaceAfterConstructor: config.insertSpaceAfterConstructor ?? false, - insertSpaceAfterSemicolonInForStatements: config.insertSpaceAfterSemicolonInForStatements ?? true, - insertSpaceBeforeAndAfterBinaryOperators: config.insertSpaceBeforeAndAfterBinaryOperators ?? true, - insertSpaceAfterKeywordsInControlFlowStatements: config.insertSpaceAfterKeywordsInControlFlowStatements ?? true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.insertSpaceAfterFunctionKeywordForAnonymousFunctions ?? true, - insertSpaceBeforeFunctionParenthesis: config.insertSpaceBeforeFunctionParenthesis ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces ?? true, - insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces ?? true, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces ?? false, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces ?? false, - insertSpaceAfterTypeAssertion: config.insertSpaceAfterTypeAssertion ?? false, - placeOpenBraceOnNewLineForFunctions: config.placeOpenBraceOnNewLineForFunctions ?? false, - placeOpenBraceOnNewLineForControlBlocks: config.placeOpenBraceOnNewLineForControlBlocks ?? false, - semicolons: config.semicolons ?? 'ignore', - }; + const defaultFormatOptions = ctx.typescript.module.getDefaultFormatCodeSettings() + + return Object.assign({}, defaultFormatOptions, filterUndefined({ + convertTabsToSpaces: options?.insertSpaces, + tabSize: options?.tabSize, + indentSize: options?.tabSize, + indentStyle: 2 /** ts.IndentStyle.Smart */, + newLineCharacter: '\n', + insertSpaceAfterCommaDelimiter: config.insertSpaceAfterCommaDelimiter ?? true, + insertSpaceAfterConstructor: config.insertSpaceAfterConstructor ?? false, + insertSpaceAfterSemicolonInForStatements: config.insertSpaceAfterSemicolonInForStatements ?? true, + insertSpaceBeforeAndAfterBinaryOperators: config.insertSpaceBeforeAndAfterBinaryOperators ?? true, + insertSpaceAfterKeywordsInControlFlowStatements: config.insertSpaceAfterKeywordsInControlFlowStatements ?? true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.insertSpaceAfterFunctionKeywordForAnonymousFunctions ?? true, + insertSpaceBeforeFunctionParenthesis: config.insertSpaceBeforeFunctionParenthesis ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces ?? true, + insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces ?? true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces ?? false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces ?? false, + insertSpaceAfterTypeAssertion: config.insertSpaceAfterTypeAssertion ?? false, + placeOpenBraceOnNewLineForFunctions: config.placeOpenBraceOnNewLineForFunctions ?? false, + placeOpenBraceOnNewLineForControlBlocks: config.placeOpenBraceOnNewLineForControlBlocks ?? false, + semicolons: config.semicolons ?? 'ignore', + })); +} + +function filterUndefined>(obj: T) { + return Object.fromEntries( + Object.entries(obj).filter(([k, v]) => v !== undefined) + ) as T; } From 255950fd8f8a7794eed0143629b66f5828c6e8d9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 13 Mar 2023 22:17:19 +0300 Subject: [PATCH 2/7] tabs --- .../src/configs/getFormatCodeSettings.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/typescript/src/configs/getFormatCodeSettings.ts b/packages/typescript/src/configs/getFormatCodeSettings.ts index 8284ea93..2252c328 100644 --- a/packages/typescript/src/configs/getFormatCodeSettings.ts +++ b/packages/typescript/src/configs/getFormatCodeSettings.ts @@ -14,32 +14,32 @@ export async function getFormatCodeSettings( config = config ?? {}; - const defaultFormatOptions = ctx.typescript.module.getDefaultFormatCodeSettings() + const defaultFormatOptions = ctx.typescript.module.getDefaultFormatCodeSettings(); return Object.assign({}, defaultFormatOptions, filterUndefined({ - convertTabsToSpaces: options?.insertSpaces, - tabSize: options?.tabSize, - indentSize: options?.tabSize, - indentStyle: 2 /** ts.IndentStyle.Smart */, - newLineCharacter: '\n', - insertSpaceAfterCommaDelimiter: config.insertSpaceAfterCommaDelimiter ?? true, - insertSpaceAfterConstructor: config.insertSpaceAfterConstructor ?? false, - insertSpaceAfterSemicolonInForStatements: config.insertSpaceAfterSemicolonInForStatements ?? true, - insertSpaceBeforeAndAfterBinaryOperators: config.insertSpaceBeforeAndAfterBinaryOperators ?? true, - insertSpaceAfterKeywordsInControlFlowStatements: config.insertSpaceAfterKeywordsInControlFlowStatements ?? true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.insertSpaceAfterFunctionKeywordForAnonymousFunctions ?? true, - insertSpaceBeforeFunctionParenthesis: config.insertSpaceBeforeFunctionParenthesis ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ?? false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces ?? true, - insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces ?? true, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces ?? false, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces ?? false, - insertSpaceAfterTypeAssertion: config.insertSpaceAfterTypeAssertion ?? false, - placeOpenBraceOnNewLineForFunctions: config.placeOpenBraceOnNewLineForFunctions ?? false, - placeOpenBraceOnNewLineForControlBlocks: config.placeOpenBraceOnNewLineForControlBlocks ?? false, - semicolons: config.semicolons ?? 'ignore', - })); + convertTabsToSpaces: options?.insertSpaces, + tabSize: options?.tabSize, + indentSize: options?.tabSize, + indentStyle: 2 /** ts.IndentStyle.Smart */, + newLineCharacter: '\n', + insertSpaceAfterCommaDelimiter: config.insertSpaceAfterCommaDelimiter ?? true, + insertSpaceAfterConstructor: config.insertSpaceAfterConstructor ?? false, + insertSpaceAfterSemicolonInForStatements: config.insertSpaceAfterSemicolonInForStatements ?? true, + insertSpaceBeforeAndAfterBinaryOperators: config.insertSpaceBeforeAndAfterBinaryOperators ?? true, + insertSpaceAfterKeywordsInControlFlowStatements: config.insertSpaceAfterKeywordsInControlFlowStatements ?? true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.insertSpaceAfterFunctionKeywordForAnonymousFunctions ?? true, + insertSpaceBeforeFunctionParenthesis: config.insertSpaceBeforeFunctionParenthesis ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ?? false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces ?? true, + insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces ?? true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces ?? false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces ?? false, + insertSpaceAfterTypeAssertion: config.insertSpaceAfterTypeAssertion ?? false, + placeOpenBraceOnNewLineForFunctions: config.placeOpenBraceOnNewLineForFunctions ?? false, + placeOpenBraceOnNewLineForControlBlocks: config.placeOpenBraceOnNewLineForControlBlocks ?? false, + semicolons: config.semicolons ?? 'ignore', + })); } function filterUndefined>(obj: T) { From fa84ca653c35c2e66325841ab89fd0b064212f37 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 16 Mar 2023 21:21:09 +0300 Subject: [PATCH 3/7] use tabs when no options are passed in --- packages/typescript/src/configs/getFormatCodeSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript/src/configs/getFormatCodeSettings.ts b/packages/typescript/src/configs/getFormatCodeSettings.ts index 2252c328..6152c57f 100644 --- a/packages/typescript/src/configs/getFormatCodeSettings.ts +++ b/packages/typescript/src/configs/getFormatCodeSettings.ts @@ -17,7 +17,7 @@ export async function getFormatCodeSettings( const defaultFormatOptions = ctx.typescript.module.getDefaultFormatCodeSettings(); return Object.assign({}, defaultFormatOptions, filterUndefined({ - convertTabsToSpaces: options?.insertSpaces, + convertTabsToSpaces: options?.insertSpaces ?? false, tabSize: options?.tabSize, indentSize: options?.tabSize, indentStyle: 2 /** ts.IndentStyle.Smart */, From 3850df46eeebebd3eedf95709172afcb2f3e72e9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 16 Mar 2023 21:24:44 +0300 Subject: [PATCH 4/7] remove leading tabs when pos in source is not indented --- .../src/services/codeActionResolve.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/typescript/src/services/codeActionResolve.ts b/packages/typescript/src/services/codeActionResolve.ts index d78b552f..6c45e8fd 100644 --- a/packages/typescript/src/services/codeActionResolve.ts +++ b/packages/typescript/src/services/codeActionResolve.ts @@ -55,7 +55,23 @@ export function resolveRefactorCodeAction( if (!editInfo) { return; } - codeAction.edit = fileTextChangesToWorkspaceEdit(editInfo.edits, ctx); + const sourceFile = ctx.typescript.languageService.getProgram()!.getSourceFile(data.fileName)!; + const patchedEdits = editInfo.edits.map(edit => { + if (edit.fileName !== data.fileName) return edit; + return { + ...edit, + textChanges: edit.textChanges.map((change) => { + const { newText, span } = change; + if (isNodeWithinBlock(ctx, sourceFile, change.span.start)) return change; + return { + newText: newText.split('\n').map(line => line.replace(/^\t/, '')).join('\n'), + span + }; + }) + }; + }); + + codeAction.edit = fileTextChangesToWorkspaceEdit(patchedEdits, ctx); if (editInfo.renameLocation !== undefined && editInfo.renameFilename !== undefined) { codeAction.command = ctx.commands.createRenameCommand( document.uri, @@ -64,6 +80,24 @@ export function resolveRefactorCodeAction( } } +function isNodeWithinBlock(ctx: SharedContext, sourceFile: ts.SourceFile, position: number): boolean | undefined { + const ts = ctx.typescript.module; + function find(node: ts.Node): boolean | undefined { + if (position >= node.getStart() && position <= node.getEnd()) { + if (ts.isBlock(node)) { + const ignoreBlock = ts.findAncestor(node.parent, (n) => ts.isBlock(n) ? 'quit' : ts.isExportAssignment(n) || ( + ts.isVariableDeclaration(n) && ts.isIdentifier(n.name) && n.name.text === '__VLS_setup' + )); + if (!ignoreBlock) return true; + } + return ts.forEachChild(node, find); + } + + return; + } + return find(sourceFile); +} + export function resolveOrganizeImportsCodeAction( ctx: SharedContext, codeAction: vscode.CodeAction, From 65866be301ce993b2bf6597868987017c441733f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 16 Mar 2023 21:56:26 +0300 Subject: [PATCH 5/7] simplify solution and fix for indented style --- .../src/services/codeActionResolve.ts | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/typescript/src/services/codeActionResolve.ts b/packages/typescript/src/services/codeActionResolve.ts index 6c45e8fd..72ca1c33 100644 --- a/packages/typescript/src/services/codeActionResolve.ts +++ b/packages/typescript/src/services/codeActionResolve.ts @@ -55,14 +55,19 @@ export function resolveRefactorCodeAction( if (!editInfo) { return; } - const sourceFile = ctx.typescript.languageService.getProgram()!.getSourceFile(data.fileName)!; + const fullText = document.getText(); const patchedEdits = editInfo.edits.map(edit => { if (edit.fileName !== data.fileName) return edit; return { ...edit, textChanges: edit.textChanges.map((change) => { + const changePos = document.positionAt(change.span.start); + const startLineOffset = document.offsetAt({ + line: changePos.line, + character: 0, + }); + if (/^\s/.test(fullText.slice(startLineOffset))) return change; const { newText, span } = change; - if (isNodeWithinBlock(ctx, sourceFile, change.span.start)) return change; return { newText: newText.split('\n').map(line => line.replace(/^\t/, '')).join('\n'), span @@ -80,24 +85,6 @@ export function resolveRefactorCodeAction( } } -function isNodeWithinBlock(ctx: SharedContext, sourceFile: ts.SourceFile, position: number): boolean | undefined { - const ts = ctx.typescript.module; - function find(node: ts.Node): boolean | undefined { - if (position >= node.getStart() && position <= node.getEnd()) { - if (ts.isBlock(node)) { - const ignoreBlock = ts.findAncestor(node.parent, (n) => ts.isBlock(n) ? 'quit' : ts.isExportAssignment(n) || ( - ts.isVariableDeclaration(n) && ts.isIdentifier(n.name) && n.name.text === '__VLS_setup' - )); - if (!ignoreBlock) return true; - } - return ts.forEachChild(node, find); - } - - return; - } - return find(sourceFile); -} - export function resolveOrganizeImportsCodeAction( ctx: SharedContext, codeAction: vscode.CodeAction, From b6ab18c85530952b34fc5405904a3adb05a65b19 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 16 Mar 2023 22:07:13 +0300 Subject: [PATCH 6/7] remove indentation only if needed, fix add braces action --- packages/typescript/src/services/codeActionResolve.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/typescript/src/services/codeActionResolve.ts b/packages/typescript/src/services/codeActionResolve.ts index 72ca1c33..35ca3db1 100644 --- a/packages/typescript/src/services/codeActionResolve.ts +++ b/packages/typescript/src/services/codeActionResolve.ts @@ -61,13 +61,13 @@ export function resolveRefactorCodeAction( return { ...edit, textChanges: edit.textChanges.map((change) => { - const changePos = document.positionAt(change.span.start); + const { newText, span } = change; + const changePos = document.positionAt(span.start); const startLineOffset = document.offsetAt({ line: changePos.line, character: 0, }); - if (/^\s/.test(fullText.slice(startLineOffset))) return change; - const { newText, span } = change; + if (/^\s/.test(fullText.slice(startLineOffset)) || !newText.split('\n')[0].startsWith('\t')) return change; return { newText: newText.split('\n').map(line => line.replace(/^\t/, '')).join('\n'), span From 65c2a15c70a8a42e02a92334e4888101e93f5298 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 24 Mar 2023 03:43:34 +0300 Subject: [PATCH 7/7] patch to inffered tabs here and patch renameLocation here --- .../src/services/codeActionResolve.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/typescript/src/services/codeActionResolve.ts b/packages/typescript/src/services/codeActionResolve.ts index cc2fc899..87834f4c 100644 --- a/packages/typescript/src/services/codeActionResolve.ts +++ b/packages/typescript/src/services/codeActionResolve.ts @@ -57,20 +57,34 @@ export function resolveRefactorCodeAction( return; } const fullText = document.getText(); + const inferredWhitespaces = document.getText().match(/^[ \t]+/m)?.[0] ?? '\t'; const patchedEdits = editInfo.edits.map(edit => { if (edit.fileName !== data.fileName) return edit; return { ...edit, textChanges: edit.textChanges.map((change) => { - const { newText, span } = change; + let { newText, span } = change; const changePos = document.positionAt(span.start); const startLineOffset = document.offsetAt({ line: changePos.line, character: 0, }); - if (/^\s/.test(fullText.slice(startLineOffset)) || !newText.split('\n')[0].startsWith('\t')) return change; + if (!/^\s/.test(fullText.slice(startLineOffset)) && newText.split('\n')[0].startsWith('\t')) { + newText = newText.split('\n').map(line => line.replace(/^\t/, '')).join('\n'); + } + // patch renameLocation for extract symbol actions (first line only) + if ( + editInfo.renameLocation && + ctx.typescript.module.textSpanContainsPosition({ + start: change.span.start, + length: newText.length, + }, editInfo.renameLocation) + ) { + const leadingWhitespaceLenDiff = inferredWhitespaces.length - 1; + editInfo.renameLocation += (newText.split('\n')[0].match(/\t/g)?.length ?? 0) * leadingWhitespaceLenDiff; + } return { - newText: newText.split('\n').map(line => line.replace(/^\t/, '')).join('\n'), + newText: newText.replaceAll('\t', inferredWhitespaces), span }; })