fix(language-service): provide hover for interpolation in attribute value (#35494)
I think the bug is introduced in my PR#34847. For example, 'model="{{title}}"', the attribute value cannot be parsed by 'parseTemplateBindings'. PR Close #35494
This commit is contained in:
parent
54fd33fb80
commit
049f118c1b
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference} from '@angular/compiler';
|
import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference} from '@angular/compiler';
|
||||||
import * as tss from 'typescript/lib/tsserverlibrary';
|
import * as tss from 'typescript/lib/tsserverlibrary';
|
||||||
|
|
||||||
import {AstResult} from './common';
|
import {AstResult} from './common';
|
||||||
@ -63,11 +63,18 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||||||
let symbol: Symbol|undefined;
|
let symbol: Symbol|undefined;
|
||||||
let span: Span|undefined;
|
let span: Span|undefined;
|
||||||
let staticSymbol: StaticSymbol|undefined;
|
let staticSymbol: StaticSymbol|undefined;
|
||||||
const attributeValueSymbol = (): boolean => {
|
const attributeValueSymbol = (ast: AST): boolean => {
|
||||||
const attribute = findAttribute(info, position);
|
const attribute = findAttribute(info, position);
|
||||||
if (attribute) {
|
if (attribute) {
|
||||||
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
||||||
const result = getSymbolInAttributeValue(info, path, attribute);
|
let result: {symbol: Symbol, span: Span}|undefined;
|
||||||
|
if (attribute.name.startsWith('*')) {
|
||||||
|
result = getSymbolInMicrosyntax(info, path, attribute);
|
||||||
|
} else {
|
||||||
|
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
||||||
|
const scope = getExpressionScope(dinfo, path);
|
||||||
|
result = getExpressionSymbol(scope, ast, templatePosition, info.template.query);
|
||||||
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
symbol = result.symbol;
|
symbol = result.symbol;
|
||||||
span = offsetSpan(result.span, attribute.valueSpan !.start.offset);
|
span = offsetSpan(result.span, attribute.valueSpan !.start.offset);
|
||||||
@ -108,13 +115,13 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||||||
},
|
},
|
||||||
visitVariable(ast) {},
|
visitVariable(ast) {},
|
||||||
visitEvent(ast) {
|
visitEvent(ast) {
|
||||||
if (!attributeValueSymbol()) {
|
if (!attributeValueSymbol(ast.handler)) {
|
||||||
symbol = findOutputBinding(ast, path, info.template.query);
|
symbol = findOutputBinding(ast, path, info.template.query);
|
||||||
symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.EVENT);
|
symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.EVENT);
|
||||||
span = spanOf(ast);
|
span = spanOf(ast);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visitElementProperty(ast) { attributeValueSymbol(); },
|
visitElementProperty(ast) { attributeValueSymbol(ast.value); },
|
||||||
visitAttr(ast) {
|
visitAttr(ast) {
|
||||||
const element = path.head;
|
const element = path.head;
|
||||||
if (!element || !(element instanceof ElementAst)) return;
|
if (!element || !(element instanceof ElementAst)) return;
|
||||||
@ -158,7 +165,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||||||
span = spanOf(ast);
|
span = spanOf(ast);
|
||||||
},
|
},
|
||||||
visitDirectiveProperty(ast) {
|
visitDirectiveProperty(ast) {
|
||||||
if (!attributeValueSymbol()) {
|
if (!attributeValueSymbol(ast.value)) {
|
||||||
const directive = findParentOfBinding(info.templateAst, ast, templatePosition);
|
const directive = findParentOfBinding(info.templateAst, ast, templatePosition);
|
||||||
const attribute = findAttribute(info, position);
|
const attribute = findAttribute(info, position);
|
||||||
if (directive && attribute) {
|
if (directive && attribute) {
|
||||||
@ -187,8 +194,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the symbol in attribute value at template position.
|
// Get the symbol in microsyntax at template position.
|
||||||
function getSymbolInAttributeValue(info: AstResult, path: TemplateAstPath, attribute: Attribute):
|
function getSymbolInMicrosyntax(info: AstResult, path: TemplateAstPath, attribute: Attribute):
|
||||||
{symbol: Symbol, span: Span}|undefined {
|
{symbol: Symbol, span: Span}|undefined {
|
||||||
if (!attribute.valueSpan) {
|
if (!attribute.valueSpan) {
|
||||||
return;
|
return;
|
||||||
|
@ -39,6 +39,16 @@ describe('hover', () => {
|
|||||||
expect(toText(displayParts)).toBe('(property) MyComponent.name: string');
|
expect(toText(displayParts)).toBe('(property) MyComponent.name: string');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to find an interpolated value in an attribute', () => {
|
||||||
|
mockHost.override(TEST_TEMPLATE, `<div string-model model="{{«title»}}"></div>`);
|
||||||
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
|
expect(quickInfo).toBeTruthy();
|
||||||
|
const {textSpan, displayParts} = quickInfo !;
|
||||||
|
expect(textSpan).toEqual(marker);
|
||||||
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to find a field in a attribute reference', () => {
|
it('should be able to find a field in a attribute reference', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user