From 24e105e3ef19d1f1f49109832b495d49f9960f40 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Fri, 3 Nov 2023 04:12:58 +0300 Subject: [PATCH 1/7] feat(special-command): migrateRequireToImports (just a shortcut for existing quickfix code action) --- package.json | 4 ++++ src/specialCommands.ts | 12 +++++++++++- typescript/src/ipcTypes.ts | 2 ++ typescript/src/specialCommands/handle.ts | 9 +++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ea49f9..1b3332a 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,10 @@ { "command": "printPerformanceMemoryInfo", "title": "Print Performance & Memory Info" + }, + { + "command": "migrateRequireToImports", + "title": "Migrate Require to Imports" } ], "keybindings": [ diff --git a/src/specialCommands.ts b/src/specialCommands.ts index c4baf7c..0d72413 100644 --- a/src/specialCommands.ts +++ b/src/specialCommands.ts @@ -7,7 +7,7 @@ import { compact } from '@zardoy/utils' import { offsetPosition } from '@zardoy/vscode-utils/build/position' import { RequestInputTypes, RequestOutputTypes } from '../typescript/src/ipcTypes' import { sendCommand } from './sendCommand' -import { tsRangeToVscode, tsRangeToVscodeSelection } from './util' +import { tsRangeToVscode, tsRangeToVscodeSelection, tsTextChangesToVscodeTextEdits } from './util' import { onCompletionAcceptedOverride } from './onCompletionAccepted' export default () => { @@ -309,6 +309,16 @@ export default () => { console.show(true) }) + registerExtensionCommand('migrateRequireToImports', async () => { + const data = await sendCommand('getMigrateToImportsEdits', {}) + if (!data) return + const { document } = vscode.window.activeTextEditor! + const edits = tsTextChangesToVscodeTextEdits(document, data) + const edit = new vscode.WorkspaceEdit() + edit.set(document.uri, edits) + await vscode.workspace.applyEdit(edit) + }) + // registerExtensionCommand('insertImportFlatten', () => { // // got -> default, got // type A = ts.Type diff --git a/typescript/src/ipcTypes.ts b/typescript/src/ipcTypes.ts index f2e93e8..91dcadc 100644 --- a/typescript/src/ipcTypes.ts +++ b/typescript/src/ipcTypes.ts @@ -17,6 +17,7 @@ export const triggerCharacterCommands = [ 'getLastResolvedCompletion', 'getArgumentReferencesFromCurrentParameter', 'performanceInfo', + 'getMigrateToImportsEdits', ] as const export type TriggerCharacterCommand = (typeof triggerCharacterCommands)[number] @@ -115,6 +116,7 @@ export type RequestOutputTypes = { } getArgumentReferencesFromCurrentParameter: Array<{ line: number; character: number; filename: string }> 'emmet-completions': EmmetResult + getMigrateToImportsEdits: ts.TextChange[] } // export type EmmetResult = { diff --git a/typescript/src/specialCommands/handle.ts b/typescript/src/specialCommands/handle.ts index fba3b12..cf8a62c 100644 --- a/typescript/src/specialCommands/handle.ts +++ b/typescript/src/specialCommands/handle.ts @@ -304,6 +304,15 @@ export default ( memoryUsedMb: toMb(process.memoryUsage().heapUsed), } } + if (specialCommand === 'getMigrateToImportsEdits') { + const combinedCodeFix = languageService.getCombinedCodeFix( + { type: 'file', fileName: sourceFile.fileName }, + 'requireInTs', + ts.getDefaultFormatCodeSettings(), + preferences, + ) + return combinedCodeFix.changes[0]?.textChanges + } return null } From 44464cbb1fde8fa2516677d801f618e2491912fa Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 23 Nov 2023 14:34:19 +0300 Subject: [PATCH 2/7] fix(vue-volar): normalize config path when checking --- src/vueVolarSupport.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vueVolarSupport.ts b/src/vueVolarSupport.ts index f937c49..346d11d 100644 --- a/src/vueVolarSupport.ts +++ b/src/vueVolarSupport.ts @@ -72,11 +72,23 @@ export default () => { const isConfigValueChanged = (settingId: string) => { if (process.env.PLATFORM !== 'web') { const config = vscode.workspace.getConfiguration('') - const userValue = config.get(settingId) - if (userValue === config.inspect(settingId)!.defaultValue) return false + let userValue = config.get(settingId) + if (!userValue || userValue === config.inspect(settingId)!.defaultValue) return false // means that value was set by us programmatically, let's update it // eslint-disable-next-line @typescript-eslint/no-require-imports - if (userValue?.startsWith(require('path').join(extensionCtx.extensionPath, '../..'))) return false + let extensionsBasePath = require('path').join(extensionCtx.extensionPath, '../..') as string + const normalizePathWin = (path: string) => { + // normalize casing of drive + if (/^[a-z]:/.test(path)) { + path = path[0]!.toUpperCase() + path.slice(1) + } + + return path.replace(/\\/g, '/') + } + + userValue = normalizePathWin(userValue) + extensionsBasePath = normalizePathWin(extensionsBasePath) + if (userValue.startsWith(extensionsBasePath)) return false return true } From 0531e41451d346f553ea6c47ce6ad807ed3ffb59 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 23 Nov 2023 14:43:30 +0300 Subject: [PATCH 3/7] fix: improve auto-renaming on file rename when import name starts with upper-case --- typescript/src/decorateEditsForFileRename.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/typescript/src/decorateEditsForFileRename.ts b/typescript/src/decorateEditsForFileRename.ts index 7aef0f7..3495784 100644 --- a/typescript/src/decorateEditsForFileRename.ts +++ b/typescript/src/decorateEditsForFileRename.ts @@ -7,14 +7,21 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService, proxy.getEditsForFileRename = (oldFilePath, newFilePath, formatOptions, preferences) => { let edits = languageService.getEditsForFileRename(oldFilePath, newFilePath, formatOptions, preferences) if (c('renameImportNameOfFileRename')) { - const predictedNameFromPath = (p: string) => camelCase(p.split(/[/\\]/g).pop()!.replace(/\..+/, '')) + const predictedNameFromPath = (p: string) => { + const input = p.split(/[/\\]/g).pop()!.replace(/\..+/, '') + const transformed = camelCase(input) + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain + const isFirstUppercase = input && input.startsWith(input[0]!.toUpperCase()) + return isFirstUppercase ? transformed[0]!.toUpperCase() + transformed.slice(1) : transformed + } const oldPredictedName = predictedNameFromPath(oldFilePath) const newPredictedName = predictedNameFromPath(newFilePath) for (const edit of edits) { const possiblyAddRename = (identifier: ts.Identifier | undefined) => { if (identifier?.text !== oldPredictedName) return const sourceFile = languageService.getProgram()!.getSourceFile(edit.fileName)! - const newRenameEdits = proxy.findRenameLocations(edit.fileName, identifier.pos, false, false, preferences ?? {}) ?? [] + const newRenameEdits = + proxy.findRenameLocations(edit.fileName, identifier.pos + identifier.getLeadingTriviaWidth(), false, false, preferences ?? {}) ?? [] if (!newRenameEdits) return // maybe cancel symbol rename on collision instead? const newInsertName = tsFull.getUniqueName(newPredictedName, sourceFile as any) From b5b816a6e9e67a7b1966a0f872ff273acb56c99e Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 28 Nov 2023 03:17:16 +0300 Subject: [PATCH 4/7] fix(filter-jsx-suggestions): local React FC declared with function declaration (named function) were not displayed in the suggestions listed --- typescript/src/completions/filterJsxComponents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/src/completions/filterJsxComponents.ts b/typescript/src/completions/filterJsxComponents.ts index ca60e94..149f6f6 100644 --- a/typescript/src/completions/filterJsxComponents.ts +++ b/typescript/src/completions/filterJsxComponents.ts @@ -76,7 +76,7 @@ export default (entries: ts.CompletionEntry[], node: ts.Node, position: number, let shouldBeCached = firstDeclarationFileName.includes('node_modules') if (!shouldBeCached && firstDeclaration.getSourceFile().fileName === fileName) { // startMark() - const definitionAtPosition = languageService.getDefinitionAtPosition(fileName, firstDeclaration.pos + 1)?.[0] + const definitionAtPosition = languageService.getDefinitionAtPosition(fileName, firstDeclaration.pos + firstDeclaration.getLeadingTriviaWidth())?.[0] // addMark('getDefinitionAtPosition') if (!definitionAtPosition) return shouldBeCached = definitionAtPosition.fileName.includes('node_modules') From 9bf21eb1d77a4bcaa6a468d49e2a9aed07fd3f1e Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 28 Nov 2023 03:17:59 +0300 Subject: [PATCH 5/7] fix prettier --- src/vueVolarSupport.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vueVolarSupport.ts b/src/vueVolarSupport.ts index 346d11d..9381dba 100644 --- a/src/vueVolarSupport.ts +++ b/src/vueVolarSupport.ts @@ -82,10 +82,10 @@ const isConfigValueChanged = (settingId: string) => { if (/^[a-z]:/.test(path)) { path = path[0]!.toUpperCase() + path.slice(1) } - + return path.replace(/\\/g, '/') } - + userValue = normalizePathWin(userValue) extensionsBasePath = normalizePathWin(extensionsBasePath) if (userValue.startsWith(extensionsBasePath)) return false From 5a1a41aba1d5b74407ec7f87eff8b410bfbaafaa Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 28 Nov 2023 03:51:26 +0300 Subject: [PATCH 6/7] feat: Speed up a bit JSX linked editing by simply caching previous response --- src/configurationType.ts | 6 ++++ typescript/src/decorateLinkedEditing.ts | 37 +++++++++++++++++++++++++ typescript/src/decorateProxy.ts | 2 ++ 3 files changed, 45 insertions(+) create mode 100644 typescript/src/decorateLinkedEditing.ts diff --git a/src/configurationType.ts b/src/configurationType.ts index b2ac606..73981b7 100644 --- a/src/configurationType.ts +++ b/src/configurationType.ts @@ -582,6 +582,12 @@ export type Configuration = { * @default false */ 'experiments.enableInsertNameOfSuggestionFix': boolean + /** + * Speed up JSX linked editing by not using actual tsserver command when possible, which in theory may introduce some inconsistencies. + * Note that currently it doesn't really help if you have `"typescript.tsserver.useSyntaxServer": "auto"` in the settings. + * @default true + */ + 'experiments.speedLinkedEditing': boolean /** * Map *symbol - array of modules* to change sorting of imports - first available takes precedence in auto import code fixes (+ import all action) * diff --git a/typescript/src/decorateLinkedEditing.ts b/typescript/src/decorateLinkedEditing.ts new file mode 100644 index 0000000..a21705d --- /dev/null +++ b/typescript/src/decorateLinkedEditing.ts @@ -0,0 +1,37 @@ +import { GetConfig } from './types' + +export default (proxy: ts.LanguageService, languageService: ts.LanguageService, languageServiceHost: ts.LanguageServiceHost, c: GetConfig) => { + // patch JSX tag linked editing to improve performance (needed for great user experience) + + let lastLinkedEditingRangeRequest: + | { + pos: number + fileName: string + result: ts.LinkedEditingInfo + } + | undefined + proxy.getLinkedEditingRangeAtPosition = (fileName, position) => { + if ( + c('experiments.speedLinkedEditing') && + lastLinkedEditingRangeRequest && + lastLinkedEditingRangeRequest.pos === position - 1 && + lastLinkedEditingRangeRequest.fileName === fileName + ) { + lastLinkedEditingRangeRequest.pos = position + lastLinkedEditingRangeRequest.result.ranges[0]!.length++ + lastLinkedEditingRangeRequest.result.ranges[1]!.start++ + lastLinkedEditingRangeRequest.result.ranges[1]!.length++ + return lastLinkedEditingRangeRequest.result + } + lastLinkedEditingRangeRequest = undefined + + const prior = languageService.getLinkedEditingRangeAtPosition(fileName, position) + if (!prior) return + lastLinkedEditingRangeRequest = { + pos: position, + fileName, + result: globalThis.structuredClone(prior), + } + return prior + } +} diff --git a/typescript/src/decorateProxy.ts b/typescript/src/decorateProxy.ts index 387c85c..6886ae7 100644 --- a/typescript/src/decorateProxy.ts +++ b/typescript/src/decorateProxy.ts @@ -19,6 +19,7 @@ import decorateSignatureHelp from './decorateSignatureHelp' import decorateFindRenameLocations from './decorateFindRenameLocations' import decorateQuickInfoAtPosition from './decorateQuickInfoAtPosition' import decorateEditsForFileRename from './decorateEditsForFileRename' +import decorateLinkedEditing from './decorateLinkedEditing' /** @internal */ export const thisPluginMarker = '__essentialPluginsMarker__' @@ -102,6 +103,7 @@ export const decorateLanguageService = ( decorateSignatureHelp(proxy, languageService, languageServiceHost, c) decorateFindRenameLocations(proxy, languageService, c) decorateQuickInfoAtPosition(proxy, languageService, languageServiceHost, c) + decorateLinkedEditing(proxy, languageService, languageServiceHost, c) libDomPatching(languageServiceHost, c) From d18c9859d54b55802ae040ce7f6cddb226b89909 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 28 Nov 2023 03:52:46 +0300 Subject: [PATCH 7/7] trigger ci