diff --git a/src/builtin-addons/core/template-completion-provider.ts b/src/builtin-addons/core/template-completion-provider.ts index d58ff47d..6fdb55fa 100644 --- a/src/builtin-addons/core/template-completion-provider.ts +++ b/src/builtin-addons/core/template-completion-provider.ts @@ -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 = _; @@ -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); @@ -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) => { @@ -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 }) => { @@ -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)) { // 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); diff --git a/src/project-roots.ts b/src/project-roots.ts index a4b46ba8..2c0c285c 100644 --- a/src/project-roots.ts +++ b/src/project-roots.ts @@ -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'; @@ -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) { diff --git a/src/utils/layout-helpers.ts b/src/utils/layout-helpers.ts index 1159ecbe..2ec4f93d 100644 --- a/src/utils/layout-helpers.ts +++ b/src/utils/layout-helpers.ts @@ -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, @@ -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; @@ -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; } diff --git a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap index 94693a19..bca38723 100644 --- a/test/__snapshots__/batman-fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/batman-fixture-based-integration-test.ts.snap @@ -5,9 +5,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "AnotherAwesomeComponent", + "label": "Boo$Bar", "textEdit": Object { - "newText": "AnotherAwesomeComponent", + "newText": "Boo$Bar", "range": Object { "end": Object { "character": 1, @@ -23,9 +23,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "MyAwesomeComponent", + "label": "Foo$Bar", "textEdit": Object { - "newText": "MyAwesomeComponent", + "newText": "Foo$Bar", "range": Object { "end": Object { "character": 1, @@ -41,9 +41,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "Nested::NestedComponent", + "label": "AnotherAwesomeComponent", "textEdit": Object { - "newText": "Nested::NestedComponent", + "newText": "AnotherAwesomeComponent", "range": Object { "end": Object { "character": 1, @@ -59,9 +59,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "Boo$Bar", + "label": "MyAwesomeComponent", "textEdit": Object { - "newText": "Boo$Bar", + "newText": "MyAwesomeComponent", "range": Object { "end": Object { "character": 1, @@ -77,9 +77,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "Foo$Bar", + "label": "Nested::NestedComponent", "textEdit": Object { - "newText": "Foo$Bar", + "newText": "Nested::NestedComponent", "range": Object { "end": Object { "character": 1, diff --git a/test/__snapshots__/fixture-based-integration-test.ts.snap b/test/__snapshots__/fixture-based-integration-test.ts.snap index c2ce1115..3fc60a4e 100644 --- a/test/__snapshots__/fixture-based-integration-test.ts.snap +++ b/test/__snapshots__/fixture-based-integration-test.ts.snap @@ -87,9 +87,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "another-awesome-component", + "label": "bar", "textEdit": Object { - "newText": "another-awesome-component", + "newText": "bar", "range": Object { "end": Object { "character": 2, @@ -105,9 +105,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "my-awesome-component", + "label": "another-awesome-component", "textEdit": Object { - "newText": "my-awesome-component", + "newText": "another-awesome-component", "range": Object { "end": Object { "character": 2, @@ -123,9 +123,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "nested/nested-component", + "label": "my-awesome-component", "textEdit": Object { - "newText": "nested/nested-component", + "newText": "my-awesome-component", "range": Object { "end": Object { "character": 2, @@ -139,11 +139,11 @@ Array [ }, }, Object { - "detail": "helper", - "kind": 3, - "label": "some-helper", + "detail": "component", + "kind": 7, + "label": "nested/nested-component", "textEdit": Object { - "newText": "some-helper", + "newText": "nested/nested-component", "range": Object { "end": Object { "character": 2, @@ -157,11 +157,11 @@ Array [ }, }, Object { - "detail": "component", - "kind": 7, - "label": "bar", + "detail": "helper", + "kind": 3, + "label": "some-helper", "textEdit": Object { - "newText": "bar", + "newText": "some-helper", "range": Object { "end": Object { "character": 2, diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index c3ea6149..bef1b6e0 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -821,9 +821,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "darling", + "label": "hello", "textEdit": Object { - "newText": "darling", + "newText": "hello", "range": Object { "end": Object { "character": 2, @@ -839,9 +839,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "hello", + "label": "darling", "textEdit": Object { - "newText": "hello", + "newText": "darling", "range": Object { "end": Object { "character": 2, @@ -938,9 +938,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "Darling", + "label": "Hello", "textEdit": Object { - "newText": "Darling", + "newText": "Hello", "range": Object { "end": Object { "character": 1, @@ -956,9 +956,9 @@ Array [ Object { "detail": "component", "kind": 7, - "label": "Hello", + "label": "Darling", "textEdit": Object { - "newText": "Hello", + "newText": "Darling", "range": Object { "end": Object { "character": 1, @@ -1196,11 +1196,11 @@ Object { Object { "detail": "component", "kind": 7, - "label": "darling", + "label": "hello", "textEdit": Object { - "newText": "darling}} + "newText": "hello}} -{{/darling}}", +{{/hello}}", "range": Object { "end": Object { "character": 3, @@ -1216,11 +1216,11 @@ Object { Object { "detail": "component", "kind": 7, - "label": "hello", + "label": "darling", "textEdit": Object { - "newText": "hello}} + "newText": "darling}} -{{/hello}}", +{{/darling}}", "range": Object { "end": Object { "character": 3, @@ -2286,192 +2286,6 @@ Object { } `; -exports[`integration GlimmerNative able to provide glimmer-native component 1`] = ` -Object { - "addonsMeta": Array [], - "registry": Object { - "component": Object { - "hello": Array [ - "app/components/hello.hbs", - ], - }, - }, - "response": Array [ - Object { - "detail": "component", - "kind": 7, - "label": "Hello", - "textEdit": Object { - "newText": "Hello", - "range": Object { - "end": Object { - "character": 1, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Button", - "textEdit": Object { - "newText": "Button", - "range": Object { - "end": Object { - "character": 1, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "ListView", - "textEdit": Object { - "newText": "ListView", - "range": Object { - "end": Object { - "character": 1, - "line": 0, - }, - "start": Object { - "character": 1, - "line": 0, - }, - }, - }, - }, - ], -} -`; - -exports[`integration GlimmerX able to provide list of locally defined components 1`] = ` -Object { - "addonsMeta": Array [], - "registry": Object {}, - "response": Array [ - Object { - "detail": "component", - "kind": 7, - "label": "App", - "textEdit": Object { - "newText": "App", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Button", - "textEdit": Object { - "newText": "Button", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Ball", - "textEdit": Object { - "newText": "Ball", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Border", - "textEdit": Object { - "newText": "Border", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Bus", - "textEdit": Object { - "newText": "Bus", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - Object { - "detail": "component", - "kind": 7, - "label": "Table", - "textEdit": Object { - "newText": "Table", - "range": Object { - "end": Object { - "character": 20, - "line": 1, - }, - "start": Object { - "character": 20, - "line": 1, - }, - }, - }, - }, - ], -} -`; - exports[`integration Go to definition works for all supported cases go to component from typescripted inline template 1`] = ` Object { "addonsMeta": Array [], @@ -2981,6 +2795,83 @@ Object { } `; +exports[`integration Project class resolution, based on fs path and file structure without ignoring main project returns one only top level result 1`] = ` +Array [ + Object { + "addonsMeta": Array [], + "registry": Object { + "component": Object { + "item": Array [ + "addon/components/item.hbs", + ], + }, + }, + "response": Array [ + Object { + "detail": "component", + "kind": 7, + "label": "Item", + "textEdit": Object { + "newText": "Item", + "range": Object { + "end": Object { + "character": 1, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, + ], + }, + Object { + "addonsMeta": Array [ + Object { + "name": "my-addon", + "root": "../lib", + "version": 1, + }, + ], + "registry": Object { + "component": Object { + "bar": Array [ + "app/components/bar.hbs", + ], + "foo": Array [ + "app/components/foo.hbs", + ], + "item": Array [ + "../lib/addon/components/item.hbs", + ], + }, + }, + "response": Array [ + Object { + "detail": "component", + "kind": 7, + "label": "Item", + "textEdit": Object { + "newText": "Item", + "range": Object { + "end": Object { + "character": 1, + "line": 0, + }, + "start": Object { + "character": 1, + "line": 0, + }, + }, + }, + }, + ], + }, +] +`; + exports[`integration autocomplete works for angle component slots 1`] = ` Array [ Object { diff --git a/test/fixtures/batman/project.json b/test/fixtures/batman/project.json index ca92d75c..9b3af4c5 100644 --- a/test/fixtures/batman/project.json +++ b/test/fixtures/batman/project.json @@ -1,39 +1,39 @@ { - "app/app.js": "import Application from '@ember/application';\nimport Resolver from './resolver';\nimport loadInitializers from 'ember-load-initializers';\nimport config from './config/environment';\n\nconst App = Application.extend({\n modulePrefix: config.modulePrefix,\n podModulePrefix: config.podModulePrefix,\n Resolver\n});\n\nloadInitializers(App, config.modulePrefix);\n\nexport default App;\n", + "app/app.js": "import Application from '@ember/application';\r\nimport Resolver from './resolver';\r\nimport loadInitializers from 'ember-load-initializers';\r\nimport config from './config/environment';\r\n\r\nconst App = Application.extend({\r\n modulePrefix: config.modulePrefix,\r\n podModulePrefix: config.podModulePrefix,\r\n Resolver\r\n});\r\n\r\nloadInitializers(App, config.modulePrefix);\r\n\r\nexport default App;\r\n", "app/components/another-awesome-component.js": "import Bar from 'foo/components/bar';\nexport default class Awesome extends Bar {\n\n}\n", "app/components/my-awesome-component.js": "", "app/helpers/some-helper.js": "", - "app/models/model-a.js": "import DS from 'ember-data';\n\nexport default DS.Model.extend({\n\n modelB: DS.hasMany('model-b'),\n\n someAttr: DS.attr('custom-transform')\n\n});\n", - "app/models/model-b.js": "import DS from 'ember-data';\n\nexport default DS.Model.extend({\n\n modelB: DS.belongsTo('model-a')\n\n});\n", - "app/resolver.js": "import Resolver from 'ember-resolver';\n\nexport default Resolver;\n", - "app/router.js": "import EmberRouter from '@ember/routing/router';\nimport config from './config/environment';\n\nconst Router = EmberRouter.extend({\n location: config.locationType,\n rootURL: config.rootURL\n});\n\nRouter.map(function() {\n this.route('test-route');\n});\n\nexport default Router;\n", + "app/models/model-a.js": "import DS from 'ember-data';\r\n\r\nexport default DS.Model.extend({\r\n\r\n modelB: DS.hasMany('model-b'),\r\n\r\n someAttr: DS.attr('custom-transform')\r\n\r\n});\r\n", + "app/models/model-b.js": "import DS from 'ember-data';\r\n\r\nexport default DS.Model.extend({\r\n\r\n modelB: DS.belongsTo('model-a')\r\n\r\n});\r\n", + "app/resolver.js": "import Resolver from 'ember-resolver';\r\n\r\nexport default Resolver;\r\n", + "app/router.js": "import EmberRouter from '@ember/routing/router';\r\nimport config from './config/environment';\r\n\r\nconst Router = EmberRouter.extend({\r\n location: config.locationType,\r\n rootURL: config.rootURL\r\n});\r\n\r\nRouter.map(function() {\r\n this.route('test-route');\r\n});\r\n\r\nexport default Router;\r\n", "app/routes/nested/nested-route.js": "", - "app/routes/test-route.js": "import Route from '@ember/routing/route';\n\nexport default Route.extend({\n});\n", - "app/templates/angle-completion.hbs": "{{! We use the next empty expression for testing completion items}}\n", "app/templates/components/another-awesome-component.hbs": "", "app/templates/components/nested/nested-component.hbs": "", - "app/templates/definition.hbs": "{{my-awesome-component}}\n{{some-helper}}\n{{link-to \"Some link\" \"\"}}\n{{#link-to \"\"}}{{/link-to}}", + "app/templates/definition.hbs": "{{my-awesome-component}}\r\n{{some-helper}}\r\n{{link-to \"Some link\" \"\"}}\r\n{{#link-to \"\"}}{{/link-to}}", "app/templates/same-component-name.hbs": "<", "app/templates/test-route.hbs": "{{outlet}}", - "app/transforms/custom-transform.js": "import DS from 'ember-data';\n\nexport default DS.Transform.extend({\n deserialize(serialized) {\n return serialized;\n },\n\n serialize(deserialized) {\n return deserialized;\n }\n});\n", - "config/environment.js": "'use strict';\n\nmodule.exports = function(environment) {\n let ENV = {\n modulePrefix: 'batman',\n environment,\n rootURL: '/',\n locationType: 'auto',\n EmberENV: {\n FEATURES: {\n // Here you can enable experimental features on an ember canary build\n // e.g. 'with-controller': true\n },\n EXTEND_PROTOTYPES: {\n // Prevent Ember Data from overriding Date.parse.\n Date: false\n }\n },\n\n APP: {\n // Here you can pass flags/options to your application instance\n // when it is created\n }\n };\n\n if (environment === 'development') {\n // ENV.APP.LOG_RESOLVER = true;\n // ENV.APP.LOG_ACTIVE_GENERATION = true;\n // ENV.APP.LOG_TRANSITIONS = true;\n // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;\n // ENV.APP.LOG_VIEW_LOOKUPS = true;\n }\n\n if (environment === 'test') {\n // Testem prefers this...\n ENV.locationType = 'none';\n\n // keep test console output quieter\n ENV.APP.LOG_ACTIVE_GENERATION = false;\n ENV.APP.LOG_VIEW_LOOKUPS = false;\n\n ENV.APP.rootElement = '#ember-testing';\n ENV.APP.autoboot = false;\n }\n\n if (environment === 'production') {\n // here you can enable a production-specific feature\n }\n\n return ENV;\n};\n", - "config/targets.js": "'use strict';\n\nconst browsers = [\n 'last 1 Chrome versions',\n 'last 1 Firefox versions',\n 'last 1 Safari versions'\n];\n\nconst isCI = !!process.env.CI;\nconst isProduction = process.env.EMBER_ENV === 'production';\n\nif (isCI || isProduction) {\n browsers.push('ie 11');\n}\n\nmodule.exports = {\n browsers\n};\n", - "ember-cli-build.js": "'use strict';\n\nconst EmberApp = require('ember-cli/lib/broccoli/ember-app');\n\nmodule.exports = function(defaults) {\n let app = new EmberApp(defaults, {\n // Add options here\n });\n\n // Use `app.import` to add additional libraries to the generated\n // output files.\n //\n // If you need to use different assets in different\n // environments, specify an object as the first parameter. That\n // object's keys should be the environment name and the values\n // should be the asset to use in that environment.\n //\n // If the library that you are including contains AMD or ES6\n // modules that you would like to import into your application\n // please specify an object with the list of modules as keys\n // along with the exports of each module as its value.\n\n return app.toTree();\n};\n", - "lib/boo/addon/templates/components/bar.hbs": "\n", - "lib/boo/index.js": "/* eslint-env node */\n'use strict';\n\nmodule.exports = {\n name: 'boo',\n\n isDevelopingAddon() {\n return true;\n }\n};", - "lib/boo/package.json": "{\n \"name\": \"boo\",\n \"keywords\": [\n \"ember-addon\"\n ],\n \"dependencies\": {\n \n }\n }", + "app/transforms/custom-transform.js": "import DS from 'ember-data';\r\n\r\nexport default DS.Transform.extend({\r\n deserialize(serialized) {\r\n return serialized;\r\n },\r\n\r\n serialize(deserialized) {\r\n return deserialized;\r\n }\r\n});\r\n", + "config/environment.js": "'use strict';\r\n\r\nmodule.exports = function(environment) {\r\n let ENV = {\r\n modulePrefix: 'batman',\r\n environment,\r\n rootURL: '/',\r\n locationType: 'auto',\r\n EmberENV: {\r\n FEATURES: {\r\n // Here you can enable experimental features on an ember canary build\r\n // e.g. 'with-controller': true\r\n },\r\n EXTEND_PROTOTYPES: {\r\n // Prevent Ember Data from overriding Date.parse.\r\n Date: false\r\n }\r\n },\r\n\r\n APP: {\r\n // Here you can pass flags/options to your application instance\r\n // when it is created\r\n }\r\n };\r\n\r\n if (environment === 'development') {\r\n // ENV.APP.LOG_RESOLVER = true;\r\n // ENV.APP.LOG_ACTIVE_GENERATION = true;\r\n // ENV.APP.LOG_TRANSITIONS = true;\r\n // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;\r\n // ENV.APP.LOG_VIEW_LOOKUPS = true;\r\n }\r\n\r\n if (environment === 'test') {\r\n // Testem prefers this...\r\n ENV.locationType = 'none';\r\n\r\n // keep test console output quieter\r\n ENV.APP.LOG_ACTIVE_GENERATION = false;\r\n ENV.APP.LOG_VIEW_LOOKUPS = false;\r\n\r\n ENV.APP.rootElement = '#ember-testing';\r\n ENV.APP.autoboot = false;\r\n }\r\n\r\n if (environment === 'production') {\r\n // here you can enable a production-specific feature\r\n }\r\n\r\n return ENV;\r\n};\r\n", + "config/targets.js": "'use strict';\r\n\r\nconst browsers = [\r\n 'last 1 Chrome versions',\r\n 'last 1 Firefox versions',\r\n 'last 1 Safari versions'\r\n];\r\n\r\nconst isCI = !!process.env.CI;\r\nconst isProduction = process.env.EMBER_ENV === 'production';\r\n\r\nif (isCI || isProduction) {\r\n browsers.push('ie 11');\r\n}\r\n\r\nmodule.exports = {\r\n browsers\r\n};\r\n", + "ember-cli-build.js": "'use strict';\r\n\r\nconst EmberApp = require('ember-cli/lib/broccoli/ember-app');\r\n\r\nmodule.exports = function(defaults) {\r\n let app = new EmberApp(defaults, {\r\n // Add options here\r\n });\r\n\r\n // Use `app.import` to add additional libraries to the generated\r\n // output files.\r\n //\r\n // If you need to use different assets in different\r\n // environments, specify an object as the first parameter. That\r\n // object's keys should be the environment name and the values\r\n // should be the asset to use in that environment.\r\n //\r\n // If the library that you are including contains AMD or ES6\r\n // modules that you would like to import into your application\r\n // please specify an object with the list of modules as keys\r\n // along with the exports of each module as its value.\r\n\r\n return app.toTree();\r\n};\r\n", + "lib/boo/addon/templates/components/bar.hbs": "\r\n", + "lib/boo/index.js": "/* eslint-env node */\r\n'use strict';\r\n\r\nmodule.exports = {\r\n name: 'boo',\r\n\r\n isDevelopingAddon() {\r\n return true;\r\n }\r\n};", + "lib/boo/package.json": "{\r\n \"name\": \"boo\",\r\n \"keywords\": [\r\n \"ember-addon\"\r\n ],\r\n \"dependencies\": {\r\n \r\n }\r\n }", "lib/foo/addon/components/bar.js": "", - "lib/foo/addon/templates/components/bar.hbs": "\n\n\n", - "lib/foo/index.js": "/* eslint-env node */\n'use strict';\n\nmodule.exports = {\n name: 'foo',\n\n isDevelopingAddon() {\n return true;\n }\n};", - "lib/foo/package.json": "{\n \"name\": \"foo\",\n \"keywords\": [\n \"ember-addon\"\n ],\n \"dependencies\": {\n \n }\n }", - "package.json": "{\n \"name\": \"batman\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"description\": \"Small description for full-project goes here\",\n \"license\": \"MIT\",\n \"author\": \"\",\n \"directories\": {\n \"doc\": \"doc\",\n \"test\": \"tests\"\n },\n \"repository\": \"\",\n \"scripts\": {\n \"build\": \"ember build\",\n \"lint:js\": \"eslint ./*.js app config lib server tests\",\n \"start\": \"ember serve\",\n \"test\": \"ember test\"\n },\n \"devDependencies\": {\n \"broccoli-asset-rev\": \"^2.4.5\",\n \"ember-ajax\": \"^3.0.0\",\n \"ember-cli\": \"~3.1.0-beta.1\",\n \"ember-cli-app-version\": \"^3.0.0\",\n \"ember-cli-babel\": \"^6.6.0\",\n \"ember-cli-dependency-checker\": \"^2.0.0\",\n \"ember-cli-eslint\": \"^4.2.1\",\n \"ember-cli-htmlbars\": \"^2.0.1\",\n \"ember-cli-htmlbars-inline-precompile\": \"^1.0.0\",\n \"ember-cli-inject-live-reload\": \"^1.4.1\",\n \"ember-cli-qunit\": \"^4.1.1\",\n \"ember-cli-shims\": \"^1.2.0\",\n \"ember-cli-sri\": \"^2.1.0\",\n \"ember-cli-uglify\": \"^2.0.0\",\n \"ember-data\": \"~3.1.0-beta.1\",\n \"ember-export-application-global\": \"^2.0.0\",\n \"ember-load-initializers\": \"^1.0.0\",\n \"ember-maybe-import-regenerator\": \"^0.1.6\",\n \"ember-resolver\": \"^4.0.0\",\n \"ember-source\": \"~3.1.0-beta.1\",\n \"ember-welcome-page\": \"^3.0.0\",\n \"eslint-plugin-ember\": \"^5.0.0\",\n \"loader.js\": \"^4.2.3\"\n },\n \"dependencies\": {\n \"ember-holy-futuristic-template-namespacing-batman\": \"^1.0.2\"\n },\n \"engines\": {\n \"node\": \"^4.5 || 6.* || >= 7.*\"\n },\n \"ember-addon\": {\n \"paths\": [\n \"lib/foo\",\n\t \"lib/boo\"\n ]\n }\n}\n", - "testem.js": "module.exports = {\n test_page: 'tests/index.html?hidepassed',\n disable_watching: true,\n launch_in_ci: [\n 'Chrome'\n ],\n launch_in_dev: [\n 'Chrome'\n ],\n browser_args: {\n Chrome: {\n mode: 'ci',\n args: [\n // --no-sandbox is needed when running Chrome inside a container\n process.env.TRAVIS ? '--no-sandbox' : null,\n\n '--disable-gpu',\n '--headless',\n '--remote-debugging-port=0',\n '--window-size=1440,900'\n ].filter(Boolean)\n }\n }\n};\n", - "tests/test-helper.js": "import Application from '../app';\nimport config from '../config/environment';\nimport { setApplication } from '@ember/test-helpers';\nimport { start } from 'ember-qunit';\n\nsetApplication(Application.create(config.APP));\n\nstart();\n", - "tests/unit/models/model-a-test.js": "import { module, test } from 'qunit';\nimport { setupTest } from 'ember-qunit';\nimport { run } from '@ember/runloop';\n\nmodule('Unit | Model | model a', function(hooks) {\n setupTest(hooks);\n\n // Replace this with your real tests.\n test('it exists', function(assert) {\n let store = this.owner.lookup('service:store');\n let model = run(() => store.createRecord('model-a', {}));\n assert.ok(model);\n });\n});\n", - "tests/unit/models/model-b-test.js": "import { module, test } from 'qunit';\nimport { setupTest } from 'ember-qunit';\nimport { run } from '@ember/runloop';\n\nmodule('Unit | Model | model b', function(hooks) {\n setupTest(hooks);\n\n // Replace this with your real tests.\n test('it exists', function(assert) {\n let store = this.owner.lookup('service:store');\n let model = run(() => store.createRecord('model-b', {}));\n assert.ok(model);\n });\n});\n", - "tests/unit/routes/test-route-test.js": "import { module, test } from 'qunit';\nimport { setupTest } from 'ember-qunit';\n\nmodule('Unit | Route | test-route', function(hooks) {\n setupTest(hooks);\n\n test('it exists', function(assert) {\n let route = this.owner.lookup('route:test-route');\n assert.ok(route);\n });\n});\n", - "tests/unit/transforms/custom-transform-test.js": "import { module, test } from 'qunit';\nimport { setupTest } from 'ember-qunit';\n\nmodule('transform:custom-transform', 'Unit | Transform | custom transform', function(hooks) {\n setupTest(hooks);\n\n // Replace this with your real tests.\n test('it exists', function(assert) {\n let transform = this.owner.lookup('transform:custom-transform');\n assert.ok(transform);\n });\n});\n" + "lib/foo/addon/templates/components/bar.hbs": "\r\n\r\n\r\n", + "lib/foo/index.js": "/* eslint-env node */\r\n'use strict';\r\n\r\nmodule.exports = {\r\n name: 'foo',\r\n\r\n isDevelopingAddon() {\r\n return true;\r\n }\r\n};", + "lib/foo/package.json": "{\r\n \"name\": \"foo\",\r\n \"keywords\": [\r\n \"ember-addon\"\r\n ],\r\n \"dependencies\": {\r\n \r\n }\r\n }", + "package.json": "{\r\n \"name\": \"batman\",\r\n \"version\": \"0.0.0\",\r\n \"private\": true,\r\n \"description\": \"Small description for full-project goes here\",\r\n \"license\": \"MIT\",\r\n \"author\": \"\",\r\n \"directories\": {\r\n \"doc\": \"doc\",\r\n \"test\": \"tests\"\r\n },\r\n \"repository\": \"\",\r\n \"scripts\": {\r\n \"build\": \"ember build\",\r\n \"lint:js\": \"eslint ./*.js app config lib server tests\",\r\n \"start\": \"ember serve\",\r\n \"test\": \"ember test\"\r\n },\r\n \"devDependencies\": {\r\n \"broccoli-asset-rev\": \"^2.4.5\",\r\n \"ember-ajax\": \"^3.0.0\",\r\n \"ember-cli\": \"~3.1.0-beta.1\",\r\n \"ember-cli-app-version\": \"^3.0.0\",\r\n \"ember-cli-babel\": \"^6.6.0\",\r\n \"ember-cli-dependency-checker\": \"^2.0.0\",\r\n \"ember-cli-eslint\": \"^4.2.1\",\r\n \"ember-cli-htmlbars\": \"^2.0.1\",\r\n \"ember-cli-htmlbars-inline-precompile\": \"^1.0.0\",\r\n \"ember-cli-inject-live-reload\": \"^1.4.1\",\r\n \"ember-cli-qunit\": \"^4.1.1\",\r\n \"ember-cli-shims\": \"^1.2.0\",\r\n \"ember-cli-sri\": \"^2.1.0\",\r\n \"ember-cli-uglify\": \"^2.0.0\",\r\n \"ember-data\": \"~3.1.0-beta.1\",\r\n \"ember-export-application-global\": \"^2.0.0\",\r\n \"ember-load-initializers\": \"^1.0.0\",\r\n \"ember-maybe-import-regenerator\": \"^0.1.6\",\r\n \"ember-resolver\": \"^4.0.0\",\r\n \"ember-source\": \"~3.1.0-beta.1\",\r\n \"ember-welcome-page\": \"^3.0.0\",\r\n \"eslint-plugin-ember\": \"^5.0.0\",\r\n \"loader.js\": \"^4.2.3\"\r\n },\r\n \"dependencies\": {\r\n \"ember-holy-futuristic-template-namespacing-batman\": \"^1.0.2\"\r\n },\r\n \"engines\": {\r\n \"node\": \"^4.5 || 6.* || >= 7.*\"\r\n },\r\n \"ember-addon\": {\r\n \"paths\": [\r\n \"lib/foo\",\r\n\t \"lib/boo\"\r\n ]\r\n }\r\n}\r\n", + "testem.js": "module.exports = {\r\n test_page: 'tests/index.html?hidepassed',\r\n disable_watching: true,\r\n launch_in_ci: [\r\n 'Chrome'\r\n ],\r\n launch_in_dev: [\r\n 'Chrome'\r\n ],\r\n browser_args: {\r\n Chrome: {\r\n mode: 'ci',\r\n args: [\r\n // --no-sandbox is needed when running Chrome inside a container\r\n process.env.TRAVIS ? '--no-sandbox' : null,\r\n\r\n '--disable-gpu',\r\n '--headless',\r\n '--remote-debugging-port=0',\r\n '--window-size=1440,900'\r\n ].filter(Boolean)\r\n }\r\n }\r\n};\r\n", + "tests/test-helper.js": "import Application from '../app';\r\nimport config from '../config/environment';\r\nimport { setApplication } from '@ember/test-helpers';\r\nimport { start } from 'ember-qunit';\r\n\r\nsetApplication(Application.create(config.APP));\r\n\r\nstart();\r\n", + "tests/unit/models/model-a-test.js": "import { module, test } from 'qunit';\r\nimport { setupTest } from 'ember-qunit';\r\nimport { run } from '@ember/runloop';\r\n\r\nmodule('Unit | Model | model a', function(hooks) {\r\n setupTest(hooks);\r\n\r\n // Replace this with your real tests.\r\n test('it exists', function(assert) {\r\n let store = this.owner.lookup('service:store');\r\n let model = run(() => store.createRecord('model-a', {}));\r\n assert.ok(model);\r\n });\r\n});\r\n", + "tests/unit/models/model-b-test.js": "import { module, test } from 'qunit';\r\nimport { setupTest } from 'ember-qunit';\r\nimport { run } from '@ember/runloop';\r\n\r\nmodule('Unit | Model | model b', function(hooks) {\r\n setupTest(hooks);\r\n\r\n // Replace this with your real tests.\r\n test('it exists', function(assert) {\r\n let store = this.owner.lookup('service:store');\r\n let model = run(() => store.createRecord('model-b', {}));\r\n assert.ok(model);\r\n });\r\n});\r\n", + "tests/unit/routes/test-route-test.js": "import { module, test } from 'qunit';\r\nimport { setupTest } from 'ember-qunit';\r\n\r\nmodule('Unit | Route | test-route', function(hooks) {\r\n setupTest(hooks);\r\n\r\n test('it exists', function(assert) {\r\n let route = this.owner.lookup('route:test-route');\r\n assert.ok(route);\r\n });\r\n});\r\n", + "tests/unit/transforms/custom-transform-test.js": "import { module, test } from 'qunit';\r\nimport { setupTest } from 'ember-qunit';\r\n\r\nmodule('transform:custom-transform', 'Unit | Transform | custom transform', function(hooks) {\r\n setupTest(hooks);\r\n\r\n // Replace this with your real tests.\r\n test('it exists', function(assert) {\r\n let transform = this.owner.lookup('transform:custom-transform');\r\n assert.ok(transform);\r\n });\r\n});\r\n" } \ No newline at end of file diff --git a/test/integration-test.ts b/test/integration-test.ts index 9ea10a31..3cd62b1d 100644 --- a/test/integration-test.ts +++ b/test/integration-test.ts @@ -175,10 +175,10 @@ describe('integration', function () { }), 'index.js': `/* eslint-env node */ 'use strict'; - + module.exports = { name: 'biz', - + isDevelopingAddon() { return true; } @@ -230,10 +230,10 @@ describe('integration', function () { }), 'index.js': `/* eslint-env node */ 'use strict'; - + module.exports = { name: 'biz', - + isDevelopingAddon() { return true; } @@ -470,32 +470,6 @@ describe('integration', function () { }); }); - describe('GlimmerX', () => { - it('able to provide list of locally defined components', async () => { - const result = await getResult( - CompletionRequest.method, - connection, - { - 'Button.ts': '', - 'Button-test.ts': '', - 'App.js': 'import hbs from "htmlbars-inline-precompile";\nexport default hbs`<`', - Components: { - 'Table.js': '', - 'Border.ts': '', - 'Border.test.ts': '', - 'Ball.jsx': '', - 'Bus.hbs': '', - }, - 'package.json': JSON.stringify({ dependencies: { '@glimmerx/core': true } }), - }, - 'App.js', - { line: 1, character: 20 } - ); - - expect(result).toMatchSnapshot(); - }); - }); - describe('DocumentSymbolProvider', () => { it('able to provide symbols for script document', async () => { const result = await getResult( @@ -567,50 +541,6 @@ describe('integration', function () { }); }); - describe('GlimmerNative', () => { - it('able to provide glimmer-native component', async () => { - const result = await getResult( - CompletionRequest.method, - connection, - { - app: { - components: { - 'hello.hbs': '<', - }, - }, - 'package.json': JSON.stringify({ dependencies: { 'glimmer-native': true } }), - node_modules: { - 'glimmer-native': { - dist: { - 'index.js': 'module.exports = () => {};', - src: { - glimmer: { - 'native-components': { - ListView: { - 'component.js': '', - }, - Button: { - 'template.js': '', - }, - }, - }, - }, - }, - 'package.json': JSON.stringify({ - name: 'glimmer-native', - main: 'dist/index.js', - }), - }, - }, - }, - 'app/components/hello.hbs', - { line: 0, character: 1 } - ); - - expect(result).toMatchSnapshot(); - }); - }); - describe('Able to provide autocomplete information for angle component arguments names', () => { it('support template-only collocated components arguments extraction', async () => { const result = await getResult( @@ -1320,10 +1250,10 @@ describe('integration', function () { }), 'index.js': `/* eslint-env node */ 'use strict'; - + module.exports = { name: 'biz', - + isDevelopingAddon() { return true; } @@ -1358,10 +1288,10 @@ describe('integration', function () { }), 'index.js': `/* eslint-env node */ 'use strict'; - + module.exports = { name: 'foo', - + isDevelopingAddon() { return true; } @@ -1544,13 +1474,19 @@ describe('integration', function () { }), }; - const result = await getResult(CompletionRequest.method, connection, files, 'lib/addon/components/item.hbs', { line: 0, character: 1 }, [ - '', + let result = await getResult(CompletionRequest.method, connection, files, 'lib/addon/components/item.hbs', { line: 0, character: 1 }, [ + 'lib', 'child-project', ]); + expect(result).toMatchSnapshot(); expect(result.length).toBe(2); expect(result[0].response.length).toBe(1); + expect(result[1].response.length).toBe(1); + + result = await getResult(CompletionRequest.method, connection, files, 'lib/addon/components/item.hbs', { line: 0, character: 1 }, ['child-project']); + + expect(result[0].response.length).toBe(3); }); it('able to ignore main project in favor of child project', async () => {