fix(compiler): absolute source span for template attribute expressions (#33189)
Prior to this commit, the absolute spans (relative to template source file rather than the start of an expression) of expressions in a template attribute like `*ngIf` were generated incorrectly, equating to the relative spans. This fixes the bug by passing an `absoluteOffset` parameter when parsing template bindings. Through some levels of indirection, this is required for the Language Service to support text replacement in https://github.com/angular/angular/pull/33091. PR Close #33189
This commit is contained in:
parent
422eb14dc0
commit
fd4fed14d8
@ -118,7 +118,7 @@ export class BindingParser {
|
|||||||
tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan, absoluteOffset: number,
|
tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan, absoluteOffset: number,
|
||||||
targetMatchableAttrs: string[][], targetProps: ParsedProperty[],
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[],
|
||||||
targetVars: ParsedVariable[]) {
|
targetVars: ParsedVariable[]) {
|
||||||
const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan);
|
const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteOffset);
|
||||||
|
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
const binding = bindings[i];
|
const binding = bindings[i];
|
||||||
@ -137,13 +137,14 @@ export class BindingParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseTemplateBindings(tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan):
|
private _parseTemplateBindings(
|
||||||
TemplateBinding[] {
|
tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan,
|
||||||
|
absoluteOffset: number): TemplateBinding[] {
|
||||||
const sourceInfo = sourceSpan.start.toString();
|
const sourceInfo = sourceSpan.start.toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const bindingsResult = this._exprParser.parseTemplateBindings(
|
const bindingsResult =
|
||||||
tplKey, tplValue, sourceInfo, sourceSpan.start.offset);
|
this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteOffset);
|
||||||
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
||||||
bindingsResult.templateBindings.forEach((binding) => {
|
bindingsResult.templateBindings.forEach((binding) => {
|
||||||
if (binding.expression) {
|
if (binding.expression) {
|
||||||
|
@ -44,6 +44,12 @@ describe('expression AST absolute source spans', () => {
|
|||||||
.toContain(['condition ? true : false', new AbsoluteSourceSpan(22, 46)]);
|
.toContain(['condition ? true : false', new AbsoluteSourceSpan(22, 46)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should provide absolute offsets of an expression in a template attribute', () => {
|
||||||
|
expect(humanizeExpressionSource(parse('<div *ngIf="value | async"></div>').nodes)).toContain([
|
||||||
|
'(value | async)', new AbsoluteSourceSpan(12, 25)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
describe('binary expression', () => {
|
describe('binary expression', () => {
|
||||||
it('should provide absolute offsets of a binary expression', () => {
|
it('should provide absolute offsets of a binary expression', () => {
|
||||||
expect(humanizeExpressionSource(parse('<div>{{1 + 2}}<div>').nodes)).toContain([
|
expect(humanizeExpressionSource(parse('<div>{{1 + 2}}<div>').nodes)).toContain([
|
||||||
|
@ -102,7 +102,10 @@ class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Visit
|
|||||||
super.visitQuote(ast, null);
|
super.visitQuote(ast, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTemplate(ast: t.Template) { t.visitAll(this, ast.children); }
|
visitTemplate(ast: t.Template) {
|
||||||
|
t.visitAll(this, ast.children);
|
||||||
|
t.visitAll(this, ast.templateAttrs);
|
||||||
|
}
|
||||||
visitElement(ast: t.Element) {
|
visitElement(ast: t.Element) {
|
||||||
t.visitAll(this, ast.children);
|
t.visitAll(this, ast.children);
|
||||||
t.visitAll(this, ast.inputs);
|
t.visitAll(this, ast.inputs);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user