mirror of
https://github.com/tenrok/BBob.git
synced 2026-06-20 20:00:33 +03:00
feat: add start and end positions of tag nodes (#246)
Closes #134 * feat: Add start and end positions of tag nodes Improves accuracy of row/col error reporting. Now targets the start of the relevant token instead of the end. * Simplify language for TagNode and Token * Update static TagNode.create to ingest setStart() logic improve readability of end pos offset for no attr tags
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { NodeContent, TagNodeObject, TagNodeTree } from "@bbob/types";
|
||||
import type { NodeContent, TagNodeObject, TagNodeTree, TagPosition } from "@bbob/types";
|
||||
|
||||
import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';
|
||||
import {
|
||||
@@ -30,38 +30,40 @@ const getTagAttrs = <AttrValue>(tag: string, params: Record<string, AttrValue>)
|
||||
const renderContent = (content: TagNodeTree, openTag: string, closeTag: string) => {
|
||||
const toString = (node: NodeContent) => {
|
||||
if (isTagNode(node)) {
|
||||
return node.toString({ openTag, closeTag })
|
||||
return node.toString({ openTag, closeTag });
|
||||
}
|
||||
|
||||
return String(node)
|
||||
}
|
||||
return String(node);
|
||||
};
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
return content.reduce<string>((r, node) => {
|
||||
if (node !== null) {
|
||||
return r + toString(node)
|
||||
return r + toString(node);
|
||||
}
|
||||
|
||||
return r
|
||||
}, '')
|
||||
return r;
|
||||
}, '');
|
||||
}
|
||||
|
||||
if (content) {
|
||||
return toString(content)
|
||||
return toString(content);
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export class TagNode<TagValue extends any = any> implements TagNodeObject {
|
||||
public readonly tag: string | TagValue
|
||||
public attrs: Record<string, unknown>
|
||||
public content: TagNodeTree
|
||||
public readonly tag: string | TagValue;
|
||||
public attrs: Record<string, unknown>;
|
||||
public content: TagNodeTree;
|
||||
public start?: TagPosition;
|
||||
public end?: TagPosition;
|
||||
|
||||
constructor(tag: string | TagValue, attrs: Record<string, unknown>, content: TagNodeTree) {
|
||||
this.tag = tag;
|
||||
this.attrs = attrs;
|
||||
this.content = content
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
attr(name: string, value?: unknown) {
|
||||
@@ -76,6 +78,14 @@ export class TagNode<TagValue extends any = any> implements TagNodeObject {
|
||||
return appendToNode(this, value);
|
||||
}
|
||||
|
||||
setStart(value: TagPosition) {
|
||||
this.start = value;
|
||||
}
|
||||
|
||||
setEnd(value: TagPosition) {
|
||||
this.end = value;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return getNodeLength(this);
|
||||
}
|
||||
@@ -91,25 +101,36 @@ export class TagNode<TagValue extends any = any> implements TagNodeObject {
|
||||
}
|
||||
|
||||
toTagNode() {
|
||||
return new TagNode(String(this.tag).toLowerCase(), this.attrs, this.content);
|
||||
const newNode = new TagNode(String(this.tag).toLowerCase(), this.attrs, this.content);
|
||||
if (this.start) {
|
||||
newNode.setStart(this.start);
|
||||
}
|
||||
if (this.end) {
|
||||
newNode.setEnd(this.end);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
|
||||
toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}): string {
|
||||
const content = this.content ? renderContent(this.content, openTag, closeTag) : ''
|
||||
const content = this.content ? renderContent(this.content, openTag, closeTag) : '';
|
||||
const tagStart = this.toTagStart({ openTag, closeTag });
|
||||
|
||||
if (this.content === null || Array.isArray(this.content) && this.content.length === 0) {
|
||||
if (this.content === null || Array.isArray(this.content) && this.content.length === 0) {
|
||||
return tagStart;
|
||||
}
|
||||
|
||||
return `${tagStart}${content}${this.toTagEnd({ openTag, closeTag })}`;
|
||||
}
|
||||
|
||||
static create(tag: string, attrs: Record<string, unknown> = {}, content: TagNodeTree = null) {
|
||||
return new TagNode(tag, attrs, content)
|
||||
static create(tag: string, attrs: Record<string, unknown> = {}, content: TagNodeTree = null, start?: TagPosition) {
|
||||
const node = new TagNode(tag, attrs, content);
|
||||
if (start) {
|
||||
node.setStart(start);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static isOf(node: TagNode, type: string) {
|
||||
return (node.tag === type)
|
||||
return (node.tag === type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { TagNode } from '../src'
|
||||
|
||||
describe('@bbob/plugin-helper/TagNode', () => {
|
||||
test('create', () => {
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello']);
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello'], {from: 0, to: 10});
|
||||
|
||||
expect(tagNode).toBeInstanceOf(TagNode)
|
||||
});
|
||||
@@ -36,12 +36,15 @@ describe('@bbob/plugin-helper/TagNode', () => {
|
||||
});
|
||||
|
||||
test('toTagNode', () => {
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello']);
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello'], {from: 0, to: 10});
|
||||
tagNode.setEnd({from: 20, to: 27});
|
||||
const newTagNode = tagNode.toTagNode()
|
||||
|
||||
expect(newTagNode !== tagNode).toBe(true);
|
||||
expect(newTagNode.tag).toEqual(tagNode.tag);
|
||||
expect(newTagNode.content).toEqual(tagNode.content);
|
||||
expect(newTagNode.start).toEqual(tagNode.start);
|
||||
expect(newTagNode.end).toEqual(tagNode.end);
|
||||
});
|
||||
|
||||
test('null content', () => {
|
||||
@@ -56,6 +59,20 @@ describe('@bbob/plugin-helper/TagNode', () => {
|
||||
expect(String(tagNode)).toBe('[img]');
|
||||
});
|
||||
|
||||
test('setStart', () => {
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello']);
|
||||
tagNode.setStart({from: 0, to: 10});
|
||||
|
||||
expect(tagNode.start).toEqual({from: 0, to: 10});
|
||||
});
|
||||
|
||||
test('setEnd', () => {
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello']);
|
||||
tagNode.setEnd({from: 20, to: 27});
|
||||
|
||||
expect(tagNode.end).toEqual({from: 20, to: 27});
|
||||
});
|
||||
|
||||
describe('toString', () => {
|
||||
test('tag with content and params', () => {
|
||||
const tagNode = TagNode.create('test', {test: 1}, ['Hello']);
|
||||
|
||||
Reference in New Issue
Block a user