Skip to content

Commit

Permalink
feat: hover provider for intl addon (translations) (#340)
Browse files Browse the repository at this point in the history
* translations hover for intl

* move `focusPath` logic out of addon into hover providers entrypoint

* test for translation autocomplete basing on translation text instead of key
  • Loading branch information
godric3 committed Nov 12, 2021
1 parent 8d1ac21 commit 3a7e6d0
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/builtin-addons/core/intl-completion-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class IntlCompletionProvider {
end: endPosition,
},
},
detail: detail,
documentation: detail,
});
}

Expand All @@ -74,7 +74,7 @@ export default class IntlCompletionProvider {
},
},
filterText: t.text + ' ' + t.locale,
detail: detail,
documentation: detail,
});
});
});
Expand Down
41 changes: 41 additions & 0 deletions src/builtin-addons/core/intl-hover-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ASTv1 } from '@glimmer/syntax';
import { Hover } from 'vscode-languageserver';
import { Server } from '../..';
import { nodeLoc } from '../../glimmer-utils';
import { HoverFunctionParams } from '../../utils/addon-api';
import { isLocalizationHelperTranslataionName } from '../../utils/ast-helpers';
import { getTranslations } from './intl-utils';

export default class IntlHoverProvider {
server: Server;
onInit(server: Server) {
this.server = server;
}

async onHover(root: string, params: HoverFunctionParams): Promise<Hover[]> {
const { results, focusPath, type } = params;

if (isLocalizationHelperTranslataionName(focusPath, type)) {
const node = focusPath.node as ASTv1.StringLiteral;
const key = node.value;
const translations = await getTranslations(root, this.server);
const location = nodeLoc(node);

Object.keys(translations).forEach((tr) => {
if (tr === key) {
const detail = translations[tr].map((t) => `${t.locale} : ${t.text}`).join('\n');

results.push({
contents: { kind: 'plaintext', value: detail },
range: {
start: { line: location.start.line - 1, character: location.start.column },
end: { line: location.end.line - 1, character: location.end.column },
},
});
}
});
}

return results;
}
}
66 changes: 63 additions & 3 deletions src/hover-provider/entry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Hover, HoverParams } from 'vscode-languageserver/node';
import { preprocess } from '@glimmer/syntax';
import { parseScriptFile } from 'ember-meta-explorer';
import { Hover, HoverParams, Position, TextDocumentIdentifier } from 'vscode-languageserver';
import { toPosition } from '../estree-utils';
import ASTPath from '../glimmer-utils';
import Server from '../server';
import { isScriptPath, isTemplatePath } from '../utils/layout-helpers';
import { logDebugInfo } from '../utils/logger';
import { queryELSAddonsAPIChain } from './../utils/addon-api';

export class HoverProvider {
constructor(private server: Server) {}
async provideHover({ textDocument, position }: HoverParams): Promise<Hover | null> {
Expand All @@ -11,17 +16,72 @@ export class HoverProvider {
return null;
}

const addonResults = await queryELSAddonsAPIChain(project.providers.hoverProviders, project.root, {
const { focusPath, type } = this.getFocusPath(textDocument, position);

if (!focusPath) {
return null;
}

const internalResults = await queryELSAddonsAPIChain(project.builtinProviders.hoverProviders, project.root, {
textDocument,
focusPath,
type,
position,
results: [],
server: this.server,
});

const addonResults = await queryELSAddonsAPIChain(project.providers.hoverProviders, project.root, {
textDocument,
focusPath,
type,
position,
results: internalResults,
server: this.server,
});

if (addonResults.length) {
return addonResults[0];
}

return null;
}

getFocusPath(textDocument: TextDocumentIdentifier, position: Position) {
const project = this.server.projectRoots.projectForUri(textDocument.uri);

if (!project) {
return {};
}

const document = this.server.documents.get(textDocument.uri);
const content = document?.getText();

if (!content) {
return {};
}

let ast = null;
let type: 'script' | 'template';

try {
if (isScriptPath(textDocument.uri)) {
ast = parseScriptFile(content);
type = 'script';
} else if (isTemplatePath(textDocument.uri)) {
ast = preprocess(content);
type = 'template';
} else {
return {};
}
} catch (e) {
logDebugInfo('error', e);

return {};
}

const focusPath = ASTPath.toPosition(ast, toPosition(position), content);

return { focusPath, type };
}
}
2 changes: 1 addition & 1 deletion src/utils/addon-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface ReferenceFunctionParams extends BaseAPIParams {
results: Location[];
}

export interface HoverFunctionParams extends BaseAPIParams {
export interface HoverFunctionParams extends ExtendedAPIParams {
results: Hover[];
}
export interface CompletionFunctionParams extends ExtendedAPIParams {
Expand Down
6 changes: 5 additions & 1 deletion src/utils/builtin-addons-initializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import IntlCompletionProvider from '../builtin-addons/core/intl-completion-provi
import { AddonMeta, ProjectProviders } from './addon-api';
import { logInfo } from './logger';
import IntlDefinitionProvider from '../builtin-addons/core/intl-definition-provider';
import IntlHoverProvider from '../builtin-addons/core/intl-hover-provider';

export function initBuiltinProviders(addonsMeta: AddonMeta[]): ProjectProviders {
const scriptDefinition = new CoreScriptDefinitionProvider();
Expand All @@ -20,6 +21,7 @@ export function initBuiltinProviders(addonsMeta: AddonMeta[]): ProjectProviders
const templateLintCommentsCodeAction = new TemplateLintCommentsCodeAction();
const typedTemplatesCodeAction = new TypedTemplatesCodeAction();
const intlDefinition = new IntlDefinitionProvider();
const intlHover = new IntlHoverProvider();

const definitionProviders = [
scriptDefinition.onDefinition.bind(scriptDefinition),
Expand All @@ -41,8 +43,10 @@ export function initBuiltinProviders(addonsMeta: AddonMeta[]): ProjectProviders
templateDefinition.onInit.bind(templateDefinition),
scriptDefinition.onInit.bind(scriptDefinition),
intlDefinition.onInit.bind(intlDefinition),
intlHover.onInit.bind(intlHover),
];
const completionProviders = [scriptCompletion.onComplete.bind(scriptCompletion), templateCompletion.onComplete.bind(templateCompletion)];
const hoverProviders = [intlHover.onHover.bind(intlHover)];

if (!addonsMeta.find((addon) => addon.name == 'els-intl-addon')) {
const intlCompletion = new IntlCompletionProvider();
Expand All @@ -57,7 +61,7 @@ export function initBuiltinProviders(addonsMeta: AddonMeta[]): ProjectProviders
definitionProviders,
referencesProviders,
codeActionProviders,
hoverProviders: [],
hoverProviders,
initFunctions,
info: [],
addonsMeta: [],
Expand Down

0 comments on commit 3a7e6d0

Please sign in to comment.