diff --git a/packages/language-server/src/ls-config.ts b/packages/language-server/src/ls-config.ts index 39e098ab3..63389e7bb 100644 --- a/packages/language-server/src/ls-config.ts +++ b/packages/language-server/src/ls-config.ts @@ -478,16 +478,25 @@ export class LSConfigManager { }; } - getTsUserPreferences(lang: TsUserConfigLang, workspacePath: string | null): ts.UserPreferences { + getTsUserPreferences( + lang: TsUserConfigLang, + normalizedWorkspacePath: string | null + ): ts.UserPreferences { const userPreferences = this.tsUserPreferences[lang]; - if (!workspacePath || !userPreferences.autoImportFileExcludePatterns) { + if (!normalizedWorkspacePath || !userPreferences.autoImportFileExcludePatterns) { return userPreferences; } - let autoImportFileExcludePatterns = this.resolvedAutoImportExcludeCache.get(workspacePath); + let autoImportFileExcludePatterns = + this.resolvedAutoImportExcludeCache.get(normalizedWorkspacePath); if (!autoImportFileExcludePatterns) { + const version = ts.version.split('.'); + const major = parseInt(version[0]); + const minor = parseInt(version[1]); + + const gte5_4 = major > 5 || (major === 5 && minor >= 4); autoImportFileExcludePatterns = userPreferences.autoImportFileExcludePatterns.map( (p) => { // Normalization rules: https://github.com/microsoft/TypeScript/pull/49578 @@ -497,17 +506,20 @@ export class LSConfigManager { return p; } - return path.join( - workspacePath, - p.startsWith('*') - ? '/' + slashNormalized - : isRelative - ? p - : '/**/' + slashNormalized - ); + // https://github.com/microsoft/vscode/pull/202762 + // ts 5.4+ supports leading wildcards + const wildcardPrefix = gte5_4 ? '' : path.parse(normalizedWorkspacePath).root; + return p.startsWith('*') + ? wildcardPrefix + slashNormalized + : isRelative + ? path.join(normalizedWorkspacePath, p) + : wildcardPrefix + '**/' + slashNormalized; } ); - this.resolvedAutoImportExcludeCache.set(workspacePath, autoImportFileExcludePatterns); + this.resolvedAutoImportExcludeCache.set( + normalizedWorkspacePath, + autoImportFileExcludePatterns + ); } return { diff --git a/packages/language-server/test/plugins/typescript/features/preferences.test.ts b/packages/language-server/test/plugins/typescript/features/preferences.test.ts index c794dc55b..b4c35f187 100644 --- a/packages/language-server/test/plugins/typescript/features/preferences.test.ts +++ b/packages/language-server/test/plugins/typescript/features/preferences.test.ts @@ -64,7 +64,14 @@ describe('ts user preferences', function () { typescript: { ...getPreferences(), ...preferences }, javascript: { ...getPreferences(), ...preferences } }); - return new LSAndTSDocResolver(docManager, [pathToUrl(testFilesDir)], configManager); + return { + lsAndTsDocResolver: new LSAndTSDocResolver( + docManager, + [pathToUrl(testFilesDir)], + configManager + ), + configManager + }; } function getDefaultPreferences(): TsUserPreferencesConfig { @@ -79,11 +86,8 @@ describe('ts user preferences', function () { it('provides auto import completion according to preferences', async () => { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -102,15 +106,13 @@ describe('ts user preferences', function () { context: CodeActionContext ) { const { docManager, document } = setup(filename); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); + const codeActionProvider = new CodeActionsProviderImpl( lsAndTsDocResolver, completionProvider, - new LSConfigManager() + configManager ); const codeAction = await codeActionProvider.getCodeActions(document, range, context); @@ -137,7 +139,7 @@ describe('ts user preferences', function () { it('provides auto import suggestions according to preferences', async () => { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { suggest: { autoImports: false, includeAutomaticOptionalChainCompletions: undefined, @@ -147,10 +149,7 @@ describe('ts user preferences', function () { includeCompletionsWithSnippetText: undefined } }); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -165,14 +164,14 @@ describe('ts user preferences', function () { function setupImportModuleSpecifierEndingJs() { const { docManager, document } = setup('module-specifier-js.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { preferences: { ...getDefaultPreferences(), importModuleSpecifierEnding: 'js' } }); - return { document, lsAndTsDocResolver }; + return { document, lsAndTsDocResolver, configManager }; } it('provides auto import for svelte component when importModuleSpecifierEnding is js', async () => { @@ -248,16 +247,13 @@ describe('ts user preferences', function () { async function testExcludeDefinitionDir(pattern: string) { const { docManager, document } = setup('code-action.svelte'); - const lsAndTsDocResolver = createLSAndTSDocResolver(docManager, { + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { preferences: { ...getDefaultPreferences(), autoImportFileExcludePatterns: [pattern] } }); - const completionProvider = new CompletionsProviderImpl( - lsAndTsDocResolver, - new LSConfigManager() - ); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); const completions = await completionProvider.getCompletions( document, @@ -280,4 +276,24 @@ describe('ts user preferences', function () { it('exclude auto import (**/ pattern)', async () => { await testExcludeDefinitionDir('**/definition'); }); + + it('exclude auto import outside of the root', async () => { + const { docManager, document } = setup('code-action-outside-root.svelte'); + const { lsAndTsDocResolver, configManager } = createLSAndTSDocResolver(docManager, { + preferences: { + ...getDefaultPreferences(), + autoImportFileExcludePatterns: ['definitions.ts'] + } + }); + const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, configManager); + + const completions = await completionProvider.getCompletions( + document, + Position.create(4, 7) + ); + + const item = completions?.items.find((item) => item.label === 'blubb'); + + assert.equal(item, undefined); + }); }); diff --git a/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte b/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte new file mode 100644 index 000000000..cb38cfe93 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/preferences/code-action-outside-root.svelte @@ -0,0 +1,4 @@ + \ No newline at end of file