Skip to content

Commit

Permalink
add element parts, startTag & endTag
Browse files Browse the repository at this point in the history
  • Loading branch information
patricklx committed Jan 25, 2024
1 parent 2ddbbc4 commit 38bec77
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as src from '../source/api';
import { generateSyntaxError } from '../syntax-error';
import traverse from '../traversal/traverse';
import Walker from '../traversal/walker';
import { appendChild, parseElementBlockParams } from '../utils';
import { appendChild, parseElementBlockParams, parseElementPartLocs } from '../utils';
import b from '../v1/parser-builders';
import publicBuilder from '../v1/public-builders';
import { HandlebarsNodeVisitors } from './handlebars-node-visitors';
Expand Down Expand Up @@ -135,6 +135,12 @@ export class TokenizerEventHandlers extends HandlebarsNodeVisitors {
blockParams: [],
loc,
});
element.startTag = {
type: 'ElementStartNode',
value: name,
loc: loc,
};
parseElementPartLocs(this.source, element);
this.elementStack.push(element);
}

Expand All @@ -143,6 +149,12 @@ export class TokenizerEventHandlers extends HandlebarsNodeVisitors {

let element = this.elementStack.pop() as ASTv1.ElementNode;

element.endTag = {
type: 'ElementEndNode',
loc: tag.loc,
value: element.selfClosing ? '' : tag.name,
};

this.validateEndTag(tag, element, isVoid);
let parent = this.currentElement();

Expand Down
21 changes: 21 additions & 0 deletions packages/@glimmer/syntax/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Nullable } from '@glimmer/interfaces';
import { expect, unwrap } from '@glimmer/util';

import type { src } from '..';
import type * as ASTv1 from './v1/api';
import type * as HBS from './v1/handlebars-ast';

Expand All @@ -20,6 +21,26 @@ export function parseElementBlockParams(element: ASTv1.ElementNode): void {
if (params) element.blockParams = params;
}

export function parseElementPartLocs(code: src.Source, element: ASTv1.ElementNode) {
const elementRange = [element.loc.getStart().offset!, element.loc.getEnd().offset!] as [
number,
number,
];
let start = elementRange[0];
let codeSlice = code.slice(...elementRange);
for (const part of element.parts) {
const idx = codeSlice.indexOf(part.value);
const range = [start + idx, 0] as [number, number];
range[1] = range[0] + part.value.length;
codeSlice = code.slice(range[1], elementRange[1]);
start = range[1];
part.loc = code.spanFor({
start: code.hbsPosFor(range[0])!,
end: code.hbsPosFor(range[1])!,
});
}
}

function parseBlockParams(element: ASTv1.ElementNode): Nullable<string[]> {
let l = element.attributes.length;
let attrNames = [];
Expand Down
21 changes: 21 additions & 0 deletions packages/@glimmer/syntax/lib/v1/nodes-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,27 @@ export interface ElementName {
loc: src.SourceLocation;
}

export interface ElementStartNode extends BaseNode {
type: 'ElementStartNode';
value: string;
}

export interface ElementEndNode extends BaseNode {
type: 'ElementEndNode';
value: string;
}

export interface ElementPartNode extends BaseNode {
type: 'ElementPartNode';
value: string;
}

export interface ElementNode extends BaseNode {
type: 'ElementNode';
tag: string;
startTag: ElementStartNode;
endTag: ElementEndNode;
parts: ElementPartNode[];
selfClosing: boolean;
attributes: AttrNode[];
blockParams: string[];
Expand Down Expand Up @@ -315,6 +333,9 @@ export type SharedNodes = {
};

export type Nodes = SharedNodes & {
ElementEndNode: ElementEndNode;
ElementStartNode: ElementStartNode;
ElementPartNode: ElementPartNode;
Program: Program;
Template: Template;
Block: Block;
Expand Down
11 changes: 11 additions & 0 deletions packages/@glimmer/syntax/lib/v1/parser-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,17 @@ class Builders {
return {
type: 'ElementNode',
tag,
startTag: {
type: 'ElementStartNode',
value: tag,
} as ASTv1.ElementStartNode,
endTag: {
type: 'ElementEndNode',
value: selfClosing ? '' : tag,
} as ASTv1.ElementEndNode,
parts: tag
.split('.')
.map((t) => ({ type: 'ElementPartNode', value: t }) as ASTv1.ElementPartNode),
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
Expand Down
11 changes: 11 additions & 0 deletions packages/@glimmer/syntax/lib/v1/public-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ function buildElement(tag: TagDescriptor, options: BuildElementOptions = {}): AS
return {
type: 'ElementNode',
tag: tagName,
startTag: {
type: 'ElementStartNode',
value: tag,
} as ASTv1.ElementStartNode,
endTag: {
type: 'ElementEndNode',
value: selfClosing ? '' : tag,
} as ASTv1.ElementEndNode,
parts: tagName
.split('.')
.map((t) => ({ type: 'ElementPartNode', value: t }) as ASTv1.ElementPartNode),
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
Expand Down
3 changes: 3 additions & 0 deletions packages/@glimmer/syntax/lib/v1/visitor-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const visitorKeys = {
CommentStatement: [],
MustacheCommentStatement: [],
ElementNode: ['attributes', 'modifiers', 'children', 'comments'],
ElementStartNode: [],
ElementPartNode: [],
ElementEndNode: [],
AttrNode: ['value'],
TextNode: [],

Expand Down
22 changes: 22 additions & 0 deletions packages/@glimmer/syntax/test/loc-node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,28 @@ test('html elements', () => {
}
});

test('html elements with paths', () => {
let ast = parse(`
<Foo as |bar|>
<bar.x.y/>
<bar.x.y></bar.x.y>
</Foo>
`);

let [, section] = ast.body;
locEqual(section, 2, 4, 5, 10, 'Foo element');
if (assertNodeType(section, 'ElementNode')) {
locEqual(section.startTag, 2, 4, 2, 18, 'Foo start tag');
locEqual(section.endTag, 5, 4, 5, 10, 'Foo end tag');
let [, barSelfClosed] = section.children;
if (assertNodeType(barSelfClosed, 'ElementNode')) {
locEqual(barSelfClosed.parts[0], 3, 7, 3, 10, 'bar.x.y bar part');
locEqual(barSelfClosed.parts[1], 3, 11, 3, 12, 'bar.x.y x part');
locEqual(barSelfClosed.parts[2], 3, 13, 3, 14, 'bar.x.y y part');
}
}
});

test('html elements with nested blocks', (assert) => {
let ast = parse(`
<div>
Expand Down
11 changes: 11 additions & 0 deletions packages/@glimmer/syntax/test/parser-node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,17 @@ export function element(tag: TagDescriptor, ...options: ElementParts[]): ASTv1.E
return {
type: 'ElementNode',
tag: tag || '',
startTag: {
type: 'ElementStartNode',
value: tag,
} as ASTv1.ElementStartNode,
endTag: {
type: 'ElementEndNode',
value: selfClosing ? '' : tag,
} as ASTv1.ElementEndNode,
parts: tag
.split('.')
.map((t) => ({ type: 'ElementPartNode', value: t }) as ASTv1.ElementPartNode),
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
Expand Down

0 comments on commit 38bec77

Please sign in to comment.