Skip to content

Commit

Permalink
fix: prevent completion using empty cache (#2258)
Browse files Browse the repository at this point in the history
The export info cache is created with an edit that doesn't trigger auto imports. And then The onFileChanged call marks the cache to be usable even though nothing is cached yet.

#2260
  • Loading branch information
jasonlyu123 committed Jan 15, 2024
1 parent 57a548c commit 7d4f8a7
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 3 deletions.
11 changes: 8 additions & 3 deletions packages/language-server/src/plugins/typescript/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,11 +719,16 @@ async function createLanguageService(

dirty = false;

if (!oldProgram) {
// https://github.com/microsoft/TypeScript/blob/23faef92703556567ddbcb9afb893f4ba638fc20/src/server/project.ts#L1624
// host.getCachedExportInfoMap will create the cache if it doesn't exist
// so we need to check the property instead
const exportMapCache = project?.exportMapCache;
if (!oldProgram || !exportMapCache || exportMapCache.isEmpty()) {
changedFilesForExportCache.clear();
return;
}

exportMapCache.releaseSymbols();
for (const fileName of changedFilesForExportCache) {
const oldFile = oldProgram.getSourceFile(fileName);
const newFile = program?.getSourceFile(fileName);
Expand All @@ -734,10 +739,10 @@ async function createLanguageService(
}

if (oldFile && newFile) {
host.getCachedExportInfoMap?.().onFileChanged?.(oldFile, newFile, false);
exportMapCache.onFileChanged?.(oldFile, newFile, false);
} else {
// new file or deleted file
host.getCachedExportInfoMap?.().clear();
exportMapCache.clear();
}
}
changedFilesForExportCache.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,55 @@ describe('CompletionProviderImpl', function () {
assert.strictEqual(detail, 'Add import from "./Bar.svelte"\n\nclass Bar');
});

it("doesn't use empty cache", async () => {
const virtualTestDir = getRandomVirtualDirPath(testFilesDir);
const { document, lsAndTsDocResolver, lsConfigManager, docManager } =
setupVirtualEnvironment({
filename: 'index.svelte',
fileContent: '<script>b</script>',
testDir: virtualTestDir
});

const completionProvider = new CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);

await lsAndTsDocResolver.getLSAndTSDoc(document);

docManager.updateDocument(document, [
{
range: Range.create(
Position.create(0, document.content.length),
Position.create(0, document.content.length)
),
text: ' '
}
]);

docManager.openClientDocument({
text: '',
uri: pathToUrl(join(virtualTestDir, 'Bar.svelte'))
});

docManager.updateDocument(document, [
{
range: Range.create(
Position.create(0, document.content.length),
Position.create(0, document.content.length)
),
text: ' '
}
]);

const completions = await completionProvider.getCompletions(document, {
line: 0,
character: 9
});

const item2 = completions?.items.find((item) => item.label === 'Bar');
const { detail } = await completionProvider.resolveCompletion(document, item2!);

assert.strictEqual(detail, 'Add import from "./Bar.svelte"\n\nclass Bar');
});

it('can auto import new export', async () => {
const virtualTestDir = getRandomVirtualDirPath(testFilesDir);
const { document, lsAndTsDocResolver, lsConfigManager, virtualSystem } =
Expand Down

0 comments on commit 7d4f8a7

Please sign in to comment.