Skip to content

Commit

Permalink
feat: improve template-completion caching (lifeart#248)
Browse files Browse the repository at this point in the history
* improve template-completion caching

* improve angle-bracket components lookup

* improve autocomplete caching

* improve typings

* improve routes autocomplete

* remove glimmer native mentions

* fix tests
  • Loading branch information
lifeart authored Apr 15, 2021
1 parent 7bba3f6 commit afb2d8d
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 399 deletions.
226 changes: 193 additions & 33 deletions src/builtin-addons/core/template-completion-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ export default class TemplateCompletionProvider {
project!: Project;
server!: Server;
hasNamespaceSupport = false;
meta = {
projectAddonsInfoInitialized: false,
helpersRegistryInitialized: false,
modifiersRegistryInitialized: false,
componentsRegistryInitialized: false,
podComponentsRegistryInitialized: false,
muComponentsRegistryInitialized: false,
routesRegistryInitialized: false,
};
enableRegistryCache(value: keyof typeof TemplateCompletionProvider.prototype['meta']) {
if (this.server.flags.hasExternalFileWatcher) {
this.meta[value] = true;
}
}
async initRegistry(_: Server, project: Project) {
this.project = project;
this.server = _;
Expand All @@ -145,10 +159,20 @@ export default class TemplateCompletionProvider {
const initStartTime = Date.now();

mListHelpers(project.root);
this.enableRegistryCache('helpersRegistryInitialized');

mListModifiers(project.root);
this.enableRegistryCache('modifiersRegistryInitialized');

mListRoutes(project.root);
this.enableRegistryCache('routesRegistryInitialized');

mListComponents(project.root);
this.enableRegistryCache('componentsRegistryInitialized');

mGetProjectAddonsInfo(project.root);
this.enableRegistryCache('projectAddonsInfoInitialized');

logInfo(project.root + ': registry initialized in ' + (Date.now() - initStartTime) + 'ms');
} catch (e) {
logError(e);
Expand All @@ -160,15 +184,38 @@ export default class TemplateCompletionProvider {
getAllAngleBracketComponents(root: string, uri: string) {
const items: CompletionItem[] = [];

if (!this.meta.projectAddonsInfoInitialized) {
mGetProjectAddonsInfo(root);
this.enableRegistryCache('projectAddonsInfoInitialized');
}

if (!this.meta.muComponentsRegistryInitialized) {
mListMUComponents(root);
this.enableRegistryCache('muComponentsRegistryInitialized');
}

if (!this.meta.componentsRegistryInitialized) {
mListComponents(root);
this.enableRegistryCache('componentsRegistryInitialized');
}

if (!this.meta.podComponentsRegistryInitialized) {
mListPodsComponents(root);
this.enableRegistryCache('podComponentsRegistryInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

return uniqBy(
items
.concat(
mListMUComponents(root),
mListComponents(root),
mListPodsComponents(root),
mListMURouteLevelComponents(root, uri),
mGetProjectAddonsInfo(root).filter(({ detail }: { detail: string }) => {
return detail === 'component';
Object.keys(registry.component).map((rawName) => {
return {
label: rawName,
kind: CompletionItemKind.Class,
detail: 'component',
};
})
)
.map((item: CompletionItem) => {
Expand All @@ -185,39 +232,103 @@ export default class TemplateCompletionProvider {
return candidates;
}
getMustachePathCandidates(root: string) {
if (!this.meta.projectAddonsInfoInitialized) {
mGetProjectAddonsInfo(root);
this.enableRegistryCache('projectAddonsInfoInitialized');
}

if (!this.meta.muComponentsRegistryInitialized) {
mListMUComponents(root);
this.enableRegistryCache('muComponentsRegistryInitialized');
}

if (!this.meta.componentsRegistryInitialized) {
mListComponents(root);
this.enableRegistryCache('componentsRegistryInitialized');
}

if (!this.meta.podComponentsRegistryInitialized) {
mListPodsComponents(root);
this.enableRegistryCache('podComponentsRegistryInitialized');
}

if (!this.meta.helpersRegistryInitialized) {
mListHelpers(root);
this.enableRegistryCache('helpersRegistryInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

const candidates: CompletionItem[] = [
...mListComponents(root),
...mListMUComponents(root),
...mListPodsComponents(root),
...mListHelpers(root),
...mGetProjectAddonsInfo(root).filter(({ detail }: { detail: string }) => {
return detail === 'component' || detail === 'helper';
...Object.keys(registry.component).map((rawName) => {
return {
label: rawName,
kind: CompletionItemKind.Class,
detail: 'component',
};
}),
...Object.keys(registry.helper).map((rawName) => {
return {
label: rawName,
kind: CompletionItemKind.Function,
detail: 'helper',
};
}),
];

return candidates;
}
getBlockPathCandidates(root: string) {
const candidates: CompletionItem[] = [
...mListComponents(root),
...mListMUComponents(root),
...mListPodsComponents(root),
...mGetProjectAddonsInfo(root).filter(({ detail }: { detail: string }) => {
return detail === 'component';
}),
];
getBlockPathCandidates(root: string): CompletionItem[] {
if (!this.meta.projectAddonsInfoInitialized) {
mGetProjectAddonsInfo(root);
this.enableRegistryCache('projectAddonsInfoInitialized');
}

return candidates;
if (!this.meta.muComponentsRegistryInitialized) {
mListMUComponents(root);
this.enableRegistryCache('muComponentsRegistryInitialized');
}

if (!this.meta.componentsRegistryInitialized) {
mListComponents(root);
this.enableRegistryCache('componentsRegistryInitialized');
}

if (!this.meta.podComponentsRegistryInitialized) {
mListPodsComponents(root);
this.enableRegistryCache('podComponentsRegistryInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

return Object.keys(registry.component).map((rawName) => {
return {
label: rawName,
kind: CompletionItemKind.Class,
detail: 'component',
};
});
}
getSubExpressionPathCandidates(root: string) {
const candidates: CompletionItem[] = [
...mListHelpers(root),
...mGetProjectAddonsInfo(root).filter(({ detail }: { detail: string }) => {
return detail === 'helper';
}),
];
if (!this.meta.helpersRegistryInitialized) {
mListHelpers(root);
this.enableRegistryCache('helpersRegistryInitialized');
}

return candidates;
if (!this.meta.projectAddonsInfoInitialized) {
mGetProjectAddonsInfo(root);
this.enableRegistryCache('projectAddonsInfoInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

return Object.keys(registry.helper).map((helperName) => {
return {
label: helperName,
kind: CompletionItemKind.Function,
detail: 'helper',
};
});
}
getScopedValues(focusPath: ASTPath) {
const scopedValues = getLocalScope(focusPath).map(({ name, node, path }) => {
Expand Down Expand Up @@ -406,18 +517,67 @@ export default class TemplateCompletionProvider {
} else if (isLinkToTarget(focusPath)) {
// {{link-to "name" "target?"}}, {{#link-to "target?"}} {{/link-to}}
log('isLinkToTarget');
completions.push(...uniqBy(mListRoutes(root), 'label'));

if (!this.meta.routesRegistryInitialized) {
mListRoutes(root);
this.enableRegistryCache('routesRegistryInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

const results = Object.keys(registry.routePath).map((name) => {
return {
label: name,
kind: CompletionItemKind.File,
detail: 'route',
};
});

completions.push(...results);
} else if (isLinkComponentRouteTarget(focusPath)) {
// <LinkTo @route="foo.." />
log('isLinkComponentRouteTarget');
completions.push(...uniqBy(mListRoutes(root), 'label'));

if (!this.meta.routesRegistryInitialized) {
mListRoutes(root);
this.enableRegistryCache('routesRegistryInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

const results = Object.keys(registry.routePath).map((name) => {
return {
label: name,
kind: CompletionItemKind.File,
detail: 'route',
};
});

completions.push(...results);
} else if (isModifierPath(focusPath)) {
log('isModifierPath');
const addonModifiers = mGetProjectAddonsInfo(root).filter(({ detail }: { detail: string }) => {
return detail === 'modifier';

if (!this.meta.modifiersRegistryInitialized) {
mListModifiers(root);
this.enableRegistryCache('modifiersRegistryInitialized');
}

if (!this.meta.projectAddonsInfoInitialized) {
mGetProjectAddonsInfo(root);
this.enableRegistryCache('projectAddonsInfoInitialized');
}

const registry = this.server.getRegistry(this.project.roots);

const resolvedModifiers = Object.keys(registry.modifier).map((name) => {
return {
label: name,
kind: CompletionItemKind.Function,
detail: 'modifier',
};
});

completions.push(...uniqBy([...emberModifierItems, ...mListModifiers(root), ...addonModifiers, ...builtinModifiers()], 'label'));
completions.push(...uniqBy([...emberModifierItems, ...resolvedModifiers, ...builtinModifiers()], 'label'));
}
} catch (e) {
log('error', e);
Expand Down
4 changes: 2 additions & 2 deletions src/project-roots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { logError, logInfo } from './utils/logger';
import * as walkSync from 'walk-sync';
import { URI } from 'vscode-uri';
import * as fs from 'fs';
import { isGlimmerNativeProject, isGlimmerXProject, isELSAddonRoot, isRootStartingWithFilePath } from './utils/layout-helpers';
import { isGlimmerXProject, isELSAddonRoot, isRootStartingWithFilePath } from './utils/layout-helpers';

import Server from './server';

Expand Down Expand Up @@ -70,7 +70,7 @@ export default class ProjectRoots {

if (filePath.endsWith('package.json')) {
try {
if (isGlimmerNativeProject(fullPath) || isGlimmerXProject(fullPath)) {
if (isGlimmerXProject(fullPath)) {
this.onProjectAdd(fullPath);
}
} catch (e) {
Expand Down
35 changes: 2 additions & 33 deletions src/utils/layout-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export function getProjectInRepoAddonsRoots(root: string) {
return roots;
}

export function listGlimmerXComponents(root: string) {
export function listGlimmerXComponents(root: string): CompletionItem[] {
try {
const jsPaths = safeWalkSync(root, {
directories: false,
Expand Down Expand Up @@ -283,28 +283,6 @@ export function listGlimmerXComponents(root: string) {
}
}

export function listGlimmerNativeComponents(root: string) {
try {
const possiblePath = resolvePackageRoot(root, 'glimmer-native', 'node_modules');

if (!possiblePath) {
return [];
}

const components = fs.readdirSync(path.join(possiblePath, 'dist', 'src', 'glimmer', 'native-components'));

return components.map((name) => {
return {
kind: CompletionItemKind.Class,
label: name,
detail: 'component',
};
});
} catch (e) {
return [];
}
}

function hasDep(pack: PackageInfo, depName: string) {
if (pack.dependencies && pack.dependencies[depName]) {
return true;
Expand Down Expand Up @@ -468,16 +446,7 @@ export function getProjectAddonsInfo(root: string) {
}
});

// log('meta', meta);
if (isGlimmerNativeProject(root)) {
meta.push(listGlimmerNativeComponents(root));
}

if (isGlimmerXProject(root)) {
meta.push(listGlimmerXComponents(root));
}

const normalizedResult: any[] = meta.reduce((arrs: any[], item: any[]) => {
const normalizedResult: CompletionItem[] = meta.reduce((arrs: CompletionItem[], item: CompletionItem[]) => {
if (!item.length) {
return arrs;
}
Expand Down
Loading

0 comments on commit afb2d8d

Please # to comment.