diff --git a/src/builtin-addons/core/template-completion-provider.ts b/src/builtin-addons/core/template-completion-provider.ts index 5e43fd60..3a296c74 100644 --- a/src/builtin-addons/core/template-completion-provider.ts +++ b/src/builtin-addons/core/template-completion-provider.ts @@ -68,6 +68,7 @@ const mListRoutes = memoize(listRoutes, { length: 1, maxAge: 60000 }); type ScopedValuesMetadata = { componentName: string; index: number; + slotName: string; }; /** @@ -395,7 +396,7 @@ export default class TemplateCompletionProvider { const [slotName, index, key] = yieldScope.split(':'); - if (slotName === 'default' && `${data.index}` === index) { + if (slotName === data.slotName && `${data.index}` === index) { const label = key.length ? `${value.label}.${key}` : `${value.label}`; const item: CompletionItem = { ...value, label }; @@ -412,7 +413,7 @@ export default class TemplateCompletionProvider { return [...scopedValues, ...extras]; } getScopedValues(focusPath: ASTPath, withMeta = false): CompletionItem[] { - const scopedValues = getLocalScope(focusPath).map(({ name, node, path, componentName, index }) => { + const scopedValues = getLocalScope(focusPath).map(({ name, node, path, componentName, slotName, index }) => { const key = node.type === 'ElementNode' ? (node as ASTv1.ElementNode).tag @@ -428,6 +429,7 @@ export default class TemplateCompletionProvider { if (withMeta) { result.data = { componentName, + slotName, index, }; } diff --git a/src/glimmer-utils.ts b/src/glimmer-utils.ts index 5f13901f..ad9d7885 100644 --- a/src/glimmer-utils.ts +++ b/src/glimmer-utils.ts @@ -111,11 +111,34 @@ class BlockParamDefinition { } get componentName() { if (this.node.type === 'ElementNode') { - return normalizeToClassicComponent((this.node as ASTv1.ElementNode).tag); + const tagName = (this.node as ASTv1.ElementNode).tag; + + if (tagName.startsWith(':')) { + const parentNode = this.path.parent as ASTv1.ElementNode; + + if (parentNode && parentNode.type === 'ElementNode') { + return normalizeToClassicComponent(parentNode.tag); + } + + return '(unknown)'; + } + + return normalizeToClassicComponent(tagName); } return '(unknown)'; } + get slotName() { + const node = this.node as ASTv1.ElementNode; + + if (node.type === 'ElementNode') { + if (node.tag.startsWith(':')) { + return node.tag.replace(':', ''); + } + } + + return 'default'; + } get index(): number { const node = this.path.node; diff --git a/src/utils/usages-api.ts b/src/utils/usages-api.ts index 984196e3..01a96c5f 100644 --- a/src/utils/usages-api.ts +++ b/src/utils/usages-api.ts @@ -150,13 +150,11 @@ async function extractTokens() { const tokens = extractTokensFromTemplate(ast); let yieldMeta = {}; - if (kind === 'component') { + if (kind === 'component' && content.includes('{{yield')) { try { yieldMeta = extractYeildMetadata(ast); } catch (e) { - yieldMeta = { - error: e.toString(), - }; + yieldMeta = {}; } } diff --git a/test/integration-test.ts b/test/integration-test.ts index 1121ecbb..7ab657aa 100644 --- a/test/integration-test.ts +++ b/test/integration-test.ts @@ -803,6 +803,32 @@ describe('integration', function () { }); describe('Able to provide autocomplete information for local scoped params', () => { + it('support named slots context autocomplete', async () => { + const result = await getResult( + CompletionRequest.method, + connection, + { + app: { + components: { + 'my-component.hbs': `{{yield (hash Foo=(component "my-component") Bar=(component "super-puper")) to="body"}}`, + 'foo.hbs': ['', '<:body as |b|>', '', ''].join('\n'), + }, + }, + $meta: { + waitForTemplateTokensToBeCollected: true, + }, + }, + 'app/components/foo.hbs', + { line: 2, character: 2 } + ); + + expect( + result.response + .map((e) => e.label) + .sort() + .join(',') + ).toBe('b,b.Bar,b.Foo'); + }); it('support tag blocks and yiled context', async () => { const result = await getResult( CompletionRequest.method,