Skip to content

Commit

Permalink
feat: each key arguments autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
lifeart committed Mar 28, 2022
1 parent 9ded549 commit 584a119
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 4 deletions.
16 changes: 13 additions & 3 deletions src/builtin-addons/core/template-completion-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import { URI } from 'vscode-uri';
import { componentsContextData } from './template-context-provider';
import { IRegistry } from '../../utils/registry-api';
import { argumentsForBuiltinComponent, docForAttribute, valuesForBuiltinComponentArgument } from '../doc/autocomplete';
import { isPathExpression } from '../../utils/ast-helpers';

const mListModifiers = memoize(listModifiers, { length: 1, maxAge: 60000 }); // 1 second
const mListComponents = memoize(listComponents, { length: 1, maxAge: 60000 }); // 1 second
Expand Down Expand Up @@ -433,6 +432,8 @@ export default class TemplateCompletionProvider {
logDebugInfo(candidates, scopedValues);
completions.push(...uniqBy([...candidates, ...scopedValues], 'label'));
} else if (isElementAttribute(focusPath) && (focusPath.node as ASTv1.AttrNode).name.startsWith('.')) {
logDebugInfo('isElementAttribute');

const attrName = '...attributes';

if (!(focusPath.parent as ASTv1.ElementNode).attributes.find((attr) => attr.name === attrName)) {
Expand All @@ -443,6 +444,8 @@ export default class TemplateCompletionProvider {
});
}
} else if (isComponentArgumentName(focusPath)) {
logDebugInfo('isComponentArgumentName');

// <Foo @name.. />

const maybeComponentName = focusPath.parent.tag;
Expand Down Expand Up @@ -501,6 +504,7 @@ export default class TemplateCompletionProvider {

completions.push(...uniqBy(candidates, 'label'));
} else if (isArgumentPathExpression(focusPath)) {
logDebugInfo('isArgumentPathExpression');
// {{@ite..}}
const rawCandidates = await this.getLocalPathExpressionCandidates(uri, originalText);
const candidates = rawCandidates.filter((el) => {
Expand Down Expand Up @@ -544,6 +548,8 @@ export default class TemplateCompletionProvider {
completions.push(...uniqBy(candidates, 'label'));
completions.push(...emberSubExpressionItems);
} else if (isPathExpression(focusPath)) {
logDebugInfo('isPathExpression');

if (isScopedPathExpression(focusPath)) {
const scopedValues = this.getScopedValues(focusPath);

Expand Down Expand Up @@ -622,7 +628,7 @@ export default class TemplateCompletionProvider {

completions.push(...uniqBy([...emberModifierItems, ...resolvedModifiers, ...builtinModifiers()], 'label'));
} else if (isHashPair(focusPath)) {
// {{#each key=value}}
// {{#each key=}}
logDebugInfo('isHashPair');

const grandPaparentNode = focusPath.parentPath?.parentPath?.node as ASTv1.BlockStatement;
Expand All @@ -643,9 +649,11 @@ export default class TemplateCompletionProvider {
}
}
} else if (isHashPairValue(focusPath)) {
// {{#each key=value}} {{/each}}
logDebugInfo('isHashPairValue');
const grandPaparentNode = focusPath.parentPath?.parentPath?.parentPath?.node as ASTv1.BlockStatement;

if (isPathExpression(grandPaparentNode.path)) {
if (grandPaparentNode && grandPaparentNode.type === 'BlockStatement' && isPathExpression(grandPaparentNode.path)) {
const name = (grandPaparentNode.path as ASTv1.PathExpression).original;

completions.push(
Expand All @@ -658,6 +666,8 @@ export default class TemplateCompletionProvider {
})
);
}
} else {
logDebugInfo('no match');
}
} catch (e) {
logError(e);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/ast-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export function isHashPair(path: ASTPath) {
}

export function isHashPairValue(path: ASTPath) {
return path.parent && isHashPair(path.parent) && path.parent.value === path.node;
return path.parent && isHashPair(path.parentPath as ASTPath) && path.parent.value === path.node;
}

export function isSubExpressionPath(path: ASTPath): boolean {
Expand Down
136 changes: 136 additions & 0 deletions test/__snapshots__/integration-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,74 @@ Object {
}
`;

exports[`integration async fs enabled: false Able to provide autocomplete information for each attributes autocomplete key argument 1`] = `
Array [
Object {
"documentation": "
The \`key\` option is used to tell Ember how to determine if the items in the
array being iterated over with \`{{#each}}\` has changed between renders. By
default the item's object identity is used.
",
"kind": 10,
"label": "key",
"textEdit": Object {
"newText": "key",
"range": Object {
"end": Object {
"character": 9,
"line": 0,
},
"start": Object {
"character": 9,
"line": 0,
},
},
},
},
]
`;
exports[`integration async fs enabled: false Able to provide autocomplete information for each attributes autocomplete key argument values 1`] = `
Array [
Object {
"documentation": "The index of the item in the array.",
"kind": 12,
"label": "@identity",
"textEdit": Object {
"newText": "@identity",
"range": Object {
"end": Object {
"character": 13,
"line": 0,
},
"start": Object {
"character": 13,
"line": 0,
},
},
},
},
Object {
"documentation": "The item in the array itself.",
"kind": 12,
"label": "@index",
"textEdit": Object {
"newText": "@index",
"range": Object {
"end": Object {
"character": 13,
"line": 0,
},
"start": Object {
"character": 13,
"line": 0,
},
},
},
},
]
`;
exports[`integration async fs enabled: false Able to provide autocomplete information for element attributes support ...attributes autocomplete 1`] = `
Object {
"addonsMeta": Array [],
Expand Down Expand Up @@ -3469,6 +3537,74 @@ Object {
}
`;
exports[`integration async fs enabled: true Able to provide autocomplete information for each attributes autocomplete key argument 1`] = `
Array [
Object {
"documentation": "
The \`key\` option is used to tell Ember how to determine if the items in the
array being iterated over with \`{{#each}}\` has changed between renders. By
default the item's object identity is used.
",
"kind": 10,
"label": "key",
"textEdit": Object {
"newText": "key",
"range": Object {
"end": Object {
"character": 9,
"line": 0,
},
"start": Object {
"character": 9,
"line": 0,
},
},
},
},
]
`;
exports[`integration async fs enabled: true Able to provide autocomplete information for each attributes autocomplete key argument values 1`] = `
Array [
Object {
"documentation": "The index of the item in the array.",
"kind": 12,
"label": "@identity",
"textEdit": Object {
"newText": "@identity",
"range": Object {
"end": Object {
"character": 13,
"line": 0,
},
"start": Object {
"character": 13,
"line": 0,
},
},
},
},
Object {
"documentation": "The item in the array itself.",
"kind": 12,
"label": "@index",
"textEdit": Object {
"newText": "@index",
"range": Object {
"end": Object {
"character": 13,
"line": 0,
},
"start": Object {
"character": 13,
"line": 0,
},
},
},
},
]
`;
exports[`integration async fs enabled: true Able to provide autocomplete information for element attributes support ...attributes autocomplete 1`] = `
Object {
"addonsMeta": Array [],
Expand Down
38 changes: 38 additions & 0 deletions test/integration-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,44 @@ describe('integration', function () {
});
});

describe('Able to provide autocomplete information for each attributes', () => {
it('autocomplete key argument', async () => {
const result = await getResult(
CompletionRequest.method,
connection,
{
app: {
components: {
'foo.hbs': ['{{#each k="" }}', '{{/each}}'].join('\n'),
},
},
},
'app/components/foo.hbs',
{ line: 0, character: 9 }
);

expect(result.response).toMatchSnapshot();
});

it('autocomplete key argument values', async () => {
const result = await getResult(
CompletionRequest.method,
connection,
{
app: {
components: {
'foo.hbs': ['{{#each key="" }}', '{{/each}}'].join('\n'),
},
},
},
'app/components/foo.hbs',
{ line: 0, character: 13 }
);

expect(result.response).toMatchSnapshot();
});
});

describe('Able to provide autocomplete information for local scoped params', () => {
it('support tag blocks', async () => {
const result = await getResult(
Expand Down

0 comments on commit 584a119

Please # to comment.