Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bumpy-buttons-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-language-server': patch
---

perf: avoid global completion in component start tag
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ interface CommitCharactersOptions {
isNewIdentifierLocation?: boolean;
}

const ENSURE_COMPONENT_HELPER = '__sveltets_2_ensureComponent';

export class CompletionsProviderImpl implements CompletionsProvider<CompletionResolveInfo> {
constructor(
private readonly lsAndTsDocResolver: LSAndTSDocResolver,
Expand Down Expand Up @@ -249,20 +251,32 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionRe
) {
const name = svelteNode.name;
const nameEnd = svelteNode.start + 1 + name.length;
const isWhitespaceAfterStartTag =
document.getText().slice(nameEnd, originalOffset).trim() === '' &&
this.mightBeAtStartTagWhitespace(document, originalOffset);

if (isWhitespaceAfterStartTag) {
// We can be sure only to get completions for directives and props here
// so don't bother with the expensive global completions
return this.getCompletionListForDirectiveOrProps(
attributeContext,
componentInfo,
wordInfo.defaultTextEditRange,
eventAndSlotLetCompletions,
tsDoc
);
const mightBeAtStartTagWhitespace = this.mightBeAtStartTagWhitespace(
document,
originalOffset
);

const generatedText = tsDoc.getFullText();
if (mightBeAtStartTagWhitespace && originalOffset > nameEnd) {
// If it's in whitespace after the component name in the start tag,
// this should trigger only directive and prop completions
// Unless it's correctly mapped to props. Handle it by ourselves to avoid expensive global completions
if (!generatedText.slice(0, offset).endsWith('props: {')) {
return this.getCompletionListForDirectiveOrProps(
attributeContext,
componentInfo,
wordInfo.defaultTextEditRange,
eventAndSlotLetCompletions,
tsDoc
);
}
} else {
const componentNameOffByOne = generatedText
.slice(0, offset + 1)
.endsWith(ENSURE_COMPONENT_HELPER + '(' + name);
if (componentNameOffByOne) {
offset++;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1880,4 +1880,50 @@ describe('CompletionProviderImpl', function () {
const item = completions?.items.find((item) => item.label === 'hi');
assert.ok(item);
});

it('provides namespace component completions for Svelte 5+', async () => {
const { completionProvider, document } = setup('namespaced-v5.svelte');

const completions = await completionProvider.getCompletions(
document,
Position.create(4, 12),
{
triggerKind: CompletionTriggerKind.Invoked
}
);

const item = completions?.items.find((item) => item.label === 'ComponentDef');
assert.ok(item);
});

it('use correct position for component tag auto-import', async () => {
const { completionProvider, document } = setup('importcompletions_unclose-tag.svelte');

const completionsAssumption = await completionProvider.getCompletions(
document,
Position.create(0, 4)
);

const itemAssumption = completionsAssumption?.items.find(
(item) => item.label === 'Completions'
);
assert.ok(
itemAssumption,
`expected this to exist. Otherwise, the "Completions" assertion is wrong`
);

const completions = await completionProvider.getCompletions(
document,
Position.create(1, 2)
);

const item = completions?.items.find((item) => item.label === 'EmptytextImported');
assert.ok(item);

const shouldNotExistItem = completions?.items.find((item) => item.label === 'Completions');
assert.ok(
!shouldNotExistItem,
'expected auto-import be filtered out by current identifier'
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<Com />
<E
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import * as Components from './ComponentDef'
</script>

<Components.