From 2408d7514cd066cb3c653afb0ab3dd6fcdd90544 Mon Sep 17 00:00:00 2001 From: Alteras1 <42795314+Alteras1@users.noreply.github.com> Date: Sun, 28 Jul 2024 22:29:12 -0700 Subject: [PATCH 1/3] 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. --- .changeset/ninety-points-explode.md | 17 + packages/bbob-core/test/index.test.ts | 68 ++- packages/bbob-parser/src/Token.ts | 42 +- packages/bbob-parser/src/lexer.ts | 31 +- packages/bbob-parser/src/parse.ts | 5 + packages/bbob-parser/src/utils.ts | 8 + packages/bbob-parser/test/Token.test.ts | 14 +- packages/bbob-parser/test/lexer.test.ts | 521 +++++++++++---------- packages/bbob-parser/test/parse.test.ts | 498 +++++++++++++++++--- packages/bbob-plugin-helper/src/TagNode.ts | 53 ++- packages/bbob-types/src/common.ts | 16 +- packages/bbob-types/src/parser.ts | 10 +- 12 files changed, 904 insertions(+), 379 deletions(-) create mode 100644 .changeset/ninety-points-explode.md diff --git a/.changeset/ninety-points-explode.md b/.changeset/ninety-points-explode.md new file mode 100644 index 00000000..93e2a294 --- /dev/null +++ b/.changeset/ninety-points-explode.md @@ -0,0 +1,17 @@ +--- +"@bbob/plugin-helper": minor +"@bbob/parser": minor +"@bbob/types": minor +"@bbob/cli": minor +"@bbob/core": minor +"@bbob/html": minor +"@bbob/preset": minor +"@bbob/preset-html5": minor +"@bbob/preset-react": minor +"@bbob/preset-vue": minor +"@bbob/react": minor +"@bbob/vue2": minor +"@bbob/vue3": minor +--- + +feat: Add start and end positions of tag nodes diff --git a/packages/bbob-core/test/index.test.ts b/packages/bbob-core/test/index.test.ts index a7923adc..b2852bfa 100644 --- a/packages/bbob-core/test/index.test.ts +++ b/packages/bbob-core/test/index.test.ts @@ -1,5 +1,5 @@ -import { TagNode } from '@bbob/parser' -import core, { BBobPluginFunction, BBobPlugins } from '../src' +import { TagNode } from '@bbob/parser'; +import core, { BBobPluginFunction, BBobPlugins } from '../src'; import { isTagNode } from "@bbob/plugin-helper"; const stringify = (val: unknown) => JSON.stringify(val); @@ -11,15 +11,17 @@ describe('@bbob/core', () => { const res = process([], '[style size="15px"]Large Text[/style]'); const ast = res.tree; - expect(res.html).toBe('[{"tag":"style","attrs":{"size":"15px"},"content":["Large"," ","Text"]}]'); + expect(res.html).toBe('[{"tag":"style","attrs":{"size":"15px"},"content":["Large"," ","Text"],"startTagPos":{"start":0,"end":19},"endTagPos":{"start":29,"end":37}}]'); expect(ast).toBeInstanceOf(Array); expect(stringify(ast)).toEqual(stringify([ { tag: 'style', attrs: { size: '15px' }, - content: ["Large", " ", "Text"] + content: ["Large", " ", "Text"], + startTagPos: { start: 0, end: 19 }, + endTagPos: { start: 29, end: 37 }, } - ])) + ])); }); test('plugin walk api node', () => { @@ -39,11 +41,11 @@ describe('@bbob/core', () => { } - return node + return node; }); - return plugin - } + return plugin; + }; const res = process([testPlugin()], '[mytag size="15px"]Large Text[/mytag]'); const ast = res.tree; @@ -61,7 +63,15 @@ describe('@bbob/core', () => { ' ', 'Text', 'Test' - ] + ], + startTagPos: { + start: 0, + end: 19 + }, + endTagPos: { + start: 29, + end: 37 + } } ])); }); @@ -71,13 +81,13 @@ describe('@bbob/core', () => { const plugin: BBobPluginFunction = (tree) => tree.walk(node => { if (node === ':)') { - return TagNode.create('test-tag', {}, []) + return TagNode.create('test-tag', {}, []); } - return node - }) + return node; + }); - return plugin + return plugin; }; const res = process([testPlugin()], '[mytag]Large Text :)[/mytag]'); @@ -99,7 +109,15 @@ describe('@bbob/core', () => { attrs: {}, content: [], } - ] + ], + startTagPos: { + start: 0, + end: 7 + }, + endTagPos: { + start: 20, + end: 28 + } } ])); }); @@ -109,13 +127,13 @@ describe('@bbob/core', () => { const plugin: BBobPluginFunction = (tree) => tree.match([{ tag: 'mytag1' }, { tag: 'mytag2' }], node => { if (isTagNode(node) && node.attrs) { - node.attrs['pass'] = 1 + node.attrs['pass'] = 1; } - return node - }) + return node; + }); - return plugin + return plugin; }; const res = process([testPlugin()], `[mytag1 size="15"]Tag1[/mytag1][mytag2 size="16"]Tag2[/mytag2][mytag3]Tag3[/mytag3]`); @@ -132,7 +150,9 @@ describe('@bbob/core', () => { }, content: [ 'Tag1' - ] + ], + startTagPos: { start: 0, end: 18 }, + endTagPos: { start: 22, end: 31 } }, { tag: 'mytag2', @@ -142,15 +162,19 @@ describe('@bbob/core', () => { }, content: [ 'Tag2' - ] + ], + startTagPos: { start: 31, end: 49 }, + endTagPos: { start: 53, end: 62 } }, { tag: 'mytag3', attrs: {}, content: [ 'Tag3' - ] + ], + startTagPos: { start: 62, end: 70 }, + endTagPos: { start: 74, end: 83 } } ])); - }) + }); }); diff --git a/packages/bbob-parser/src/Token.ts b/packages/bbob-parser/src/Token.ts index da1008d1..8da06d7c 100644 --- a/packages/bbob-parser/src/Token.ts +++ b/packages/bbob-parser/src/Token.ts @@ -5,12 +5,14 @@ import { } from '@bbob/plugin-helper'; import type { Token as TokenInterface } from "@bbob/types"; -// type, value, line, row, +// type, value, line, row, start pos, end pos const TOKEN_TYPE_ID = 't'; // 0; const TOKEN_VALUE_ID = 'v'; // 1; const TOKEN_COLUMN_ID = 'r'; // 2; const TOKEN_LINE_ID = 'l'; // 3; +const TOKEN_START_POS_ID = 's'; // 4; +const TOKEN_END_POS_ID = 'e'; // 5; const TOKEN_TYPE_WORD = 1; // 'word'; const TOKEN_TYPE_TAG = 2; // 'tag'; @@ -31,11 +33,15 @@ const getTokenLine = (token: Token) => (token && token[TOKEN_LINE_ID]) || 0; const getTokenColumn = (token: Token) => (token && token[TOKEN_COLUMN_ID]) || 0; +const getStartPosition = (token: Token) => (token && token[TOKEN_START_POS_ID]) || 0; + +const getEndPosition = (token: Token) => (token && token[TOKEN_END_POS_ID]) || 0; + const isTextToken = (token: Token) => { if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE - || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE - || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD; + || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE + || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD; } return false; @@ -88,21 +94,25 @@ const tokenToText = (token: Token) => { * @export * @class Token */ -class Token implements TokenInterface { - readonly t: number // type - readonly v: string // value - readonly l: number // line - readonly r: number // row - - constructor(type?: number, value?: TokenValue, row: number = 0, col: number = 0) { +class Token implements TokenInterface { + readonly t: number; // type + readonly v: string; // value + readonly l: number; // line + readonly r: number; // row + readonly s: number; // start pos + readonly e: number; // end pos + + constructor(type?: number, value?: TokenValue, row: number = 0, col: number = 0, start: number = 0, end: number = 0) { this[TOKEN_LINE_ID] = row; this[TOKEN_COLUMN_ID] = col; this[TOKEN_TYPE_ID] = type || 0; this[TOKEN_VALUE_ID] = String(value); + this[TOKEN_START_POS_ID] = start; + this[TOKEN_END_POS_ID] = end; } get type() { - return this[TOKEN_TYPE_ID] + return this[TOKEN_TYPE_ID]; } isEmpty() { @@ -149,6 +159,14 @@ class Token implements TokenInterface { return getTokenColumn(this); } + getStartPos() { + return getStartPosition(this); + } + + getEndPos() { + return getEndPosition(this); + } + toString() { return tokenToText(this); } @@ -158,6 +176,8 @@ export const TYPE_ID = TOKEN_TYPE_ID; export const VALUE_ID = TOKEN_VALUE_ID; export const LINE_ID = TOKEN_LINE_ID; export const COLUMN_ID = TOKEN_COLUMN_ID; +export const START_POS_ID = TOKEN_START_POS_ID; +export const END_POS_ID = TOKEN_END_POS_ID; export const TYPE_WORD = TOKEN_TYPE_WORD; export const TYPE_TAG = TOKEN_TYPE_TAG; export const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME; diff --git a/packages/bbob-parser/src/lexer.ts b/packages/bbob-parser/src/lexer.ts index 83634733..10c7d0aa 100644 --- a/packages/bbob-parser/src/lexer.ts +++ b/packages/bbob-parser/src/lexer.ts @@ -20,8 +20,8 @@ import { CharGrabber, createCharGrabber, trimChar, unquote } from './utils'; // for cases const EM = '!'; -export function createTokenOfType(type: number, value: string, r = 0, cl = 0) { - return new Token(type, value, r, cl) +export function createTokenOfType(type: number, value: string, r = 0, cl = 0, p = 0, e = 0) { + return new Token(type, value, r, cl, p, e); } const STATE_WORD = 0; @@ -43,6 +43,7 @@ const unq = (val: string) => unquote(trimChar(val, QUOTEMARK)); export function createLexer(buffer: string, options: LexerOptions = {}): LexerTokenizer { let row = 0; + let prevCol = 0; let col = 0; let tokenIndex = -1; @@ -89,16 +90,17 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo * @param {Number} type * @param {String} value */ - function emitToken(type: number, value: string) { - const token = createTokenOfType(type, value, row, col); + function emitToken(type: number, value: string, startPos?: number, endPos?: number) { + const token = createTokenOfType(type, value, row, prevCol, startPos, endPos); onToken(token); + prevCol = col; tokenIndex += 1; tokens[tokenIndex] = token; } - function nextTagState(tagChars: CharGrabber, isSingleValueTag: boolean) { + function nextTagState(tagChars: CharGrabber, isSingleValueTag: boolean, masterStartPos: number) { if (tagMode === TAG_STATE_ATTR) { const validAttrName = (char: string) => !(char === EQ || isWhiteSpace(char)); const name = tagChars.grabWhile(validAttrName); @@ -161,6 +163,9 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo tagChars.skip(); emitToken(TYPE_ATTR_VALUE, unq(name)); + if (tagChars.getPrev() === QUOTEMARK) { + prevCol++; + } if (tagChars.isLast()) { return TAG_STATE_NAME; @@ -169,13 +174,15 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo return TAG_STATE_ATTR; } + const start = masterStartPos + tagChars.getPos() - 1; const validName = (char: string) => !(char === EQ || isWhiteSpace(char) || tagChars.isLast()); const name = tagChars.grabWhile(validName); - emitToken(TYPE_TAG, name); + emitToken(TYPE_TAG, name, start, masterStartPos + tagChars.getLength() + 1); checkContextFreeMode(name); tagChars.skip(); + prevCol++; // in cases when we has [url=someval]GET[/url] and we dont need to parse all if (isSingleValueTag) { @@ -209,11 +216,12 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo const isClosingTag = substr[0] === SLASH; if (isNoAttrsInTag || isClosingTag) { + const startPos = chars.getPos() - 1; const name = chars.grabWhile((char) => char !== closeTag); chars.skip(); // skip closeTag - emitToken(TYPE_TAG, name); + emitToken(TYPE_TAG, name, startPos, startPos + name.length + 2); checkContextFreeMode(name, isClosingTag); return STATE_WORD; @@ -223,6 +231,7 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo } function stateAttrs() { + const startPos = chars.getPos(); const silent = true; const tagStr = chars.grabWhile((char) => char !== closeTag, silent); const tagGrabber = createCharGrabber(tagStr, { onSkip }); @@ -231,7 +240,7 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo tagMode = TAG_STATE_NAME; while (tagGrabber.hasNext()) { - tagMode = nextTagState(tagGrabber, !hasSpace); + tagMode = nextTagState(tagGrabber, !hasSpace, startPos); } chars.skip(); // skip closeTag @@ -246,6 +255,7 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo chars.skip(); col = 0; + prevCol = 0; row++; return STATE_WORD; @@ -276,6 +286,7 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo emitToken(TYPE_WORD, chars.getCurr()); chars.skip(); + prevCol++; return STATE_WORD; } @@ -345,7 +356,7 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo if (nestedMap.has(value)) { return !!nestedMap.get(value); } else { - const status = (buffer.indexOf(value) > -1) + const status = (buffer.indexOf(value) > -1); nestedMap.set(value, status); @@ -356,5 +367,5 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo return { tokenize, isTokenNested, - } + }; } diff --git a/packages/bbob-parser/src/parse.ts b/packages/bbob-parser/src/parse.ts index e1a07597..bceea1bd 100644 --- a/packages/bbob-parser/src/parse.ts +++ b/packages/bbob-parser/src/parse.ts @@ -186,6 +186,7 @@ function parse(input: string, opts: ParseOptions = {}) { flushTagNodes(); const tagNode = TagNode.create(token.getValue(), {}, []); + tagNode.setStartTagPos(token.getStartPos(), token.getEndPos()); const isNested = isTokenNested(token); tagNodes.push(tagNode); @@ -203,6 +204,10 @@ function parse(input: string, opts: ParseOptions = {}) { * @param {Token} token */ function handleTagEnd(token: Token) { + const lastTagNode = nestedNodes.last(); + if (isTagNode(lastTagNode)) { + lastTagNode.setEndTagPos(token.getStartPos(), token.getEndPos()); + } flushTagNodes(); const lastNestedNode = nestedNodes.flush(); diff --git a/packages/bbob-parser/src/utils.ts b/packages/bbob-parser/src/utils.ts index d66a56e4..0873a6b9 100644 --- a/packages/bbob-parser/src/utils.ts +++ b/packages/bbob-parser/src/utils.ts @@ -42,6 +42,14 @@ export class CharGrabber { return this.s[this.c.pos] } + getPos() { + return this.c.pos; + } + + getLength() { + return this.c.len; + } + getRest() { return this.s.substring(this.c.pos) } diff --git a/packages/bbob-parser/test/Token.test.ts b/packages/bbob-parser/test/Token.test.ts index f68c91ec..05162214 100644 --- a/packages/bbob-parser/test/Token.test.ts +++ b/packages/bbob-parser/test/Token.test.ts @@ -1,10 +1,10 @@ -import Token, { TYPE_WORD, TYPE_TAG, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_SPACE, TYPE_NEW_LINE } from '../src/Token' +import Token, { TYPE_WORD, TYPE_TAG, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_SPACE, TYPE_NEW_LINE } from '../src/Token'; describe('Token', () => { test('isEmpty', () => { const token = new Token(); - expect(token.isEmpty()).toBeTruthy() + expect(token.isEmpty()).toBeTruthy(); }); test('isText', () => { const token = new Token(TYPE_WORD); @@ -56,6 +56,16 @@ describe('Token', () => { expect(token.getColumn()).toBe(14); }); + test('getStartPos', () => { + const token = new Token(TYPE_TAG, 'my-tag', 12, 14, 50); + + expect(token.getStartPos()).toBe(50); + }); + test('getEndPos', () => { + const token = new Token(TYPE_TAG, 'my-tag', 12, 14, 50, 60); + + expect(token.getEndPos()).toBe(60); + }); test('toString', () => { const tokenEnd = new Token(TYPE_TAG, '/my-tag', 12, 14); diff --git a/packages/bbob-parser/test/lexer.test.ts b/packages/bbob-parser/test/lexer.test.ts index 0b6b16ba..49e89317 100644 --- a/packages/bbob-parser/test/lexer.test.ts +++ b/packages/bbob-parser/test/lexer.test.ts @@ -1,5 +1,5 @@ -import { TYPE_ID, VALUE_ID, TYPE_WORD, TYPE_TAG, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_SPACE, TYPE_NEW_LINE} from '../src/Token' -import { createLexer } from '../src/lexer' +import { TYPE_ID, VALUE_ID, TYPE_WORD, TYPE_TAG, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_SPACE, TYPE_NEW_LINE, LINE_ID, COLUMN_ID, START_POS_ID, END_POS_ID } from '../src/Token'; +import { createLexer } from '../src/lexer'; declare global { namespace jest { @@ -32,19 +32,19 @@ describe('lexer', () => { if (tokens.length !== output.length) { return { message: () => - `expected tokens length ${tokens.length} to be ${output.length}`, + `expected tokens length ${tokens.length} to be ${output.length}`, pass: false, }; } for (let idx = 0; idx < tokens.length; idx++) { const token = tokens[idx]; - const [type, value] = output[idx]; + const [type, value, col, row, startPos, endPos] = output[idx]; if (typeof token !== 'object') { return { message: () => - `token must to be Object`, + `token must to be Object`, pass: false, }; } @@ -52,7 +52,7 @@ describe('lexer', () => { if (token[TYPE_ID] !== type) { return { message: () => - `expected token type ${TYPE_NAMES[type]} but recieved ${TYPE_NAMES[token[TYPE_ID]]} for ${JSON.stringify(output[idx])}`, + `expected token type ${TYPE_NAMES[type]} but received ${TYPE_NAMES[token[TYPE_ID]]} for ${JSON.stringify(output[idx])}`, pass: false, }; } @@ -60,7 +60,39 @@ describe('lexer', () => { if (token[VALUE_ID] !== value) { return { message: () => - `expected token value ${value} but recieved ${token[VALUE_ID]} for ${JSON.stringify(output[idx])}`, + `expected token value ${value} but received ${token[VALUE_ID]} for ${JSON.stringify(output[idx])}`, + pass: false, + }; + } + + if (token[LINE_ID] !== row) { + return { + message: () => + `expected token row ${row} but received ${token[LINE_ID]} for ${JSON.stringify(output[idx])}`, + pass: false, + }; + } + + if (token[COLUMN_ID] !== col) { + return { + message: () => + `expected token col ${col} but received ${token[COLUMN_ID]} for ${JSON.stringify(output[idx])}`, + pass: false, + }; + } + + if (type === TYPE.TAG && token[START_POS_ID] !== startPos) { + return { + message: () => + `expected token start pos ${startPos} but received ${token[START_POS_ID]} for ${JSON.stringify(output[idx])}`, + pass: false, + }; + } + + if (type === TYPE.TAG && token[END_POS_ID] !== endPos) { + return { + message: () => + `expected token end pos ${endPos} but received ${token[END_POS_ID]} for ${JSON.stringify(output[idx])}`, pass: false, }; } @@ -68,7 +100,7 @@ describe('lexer', () => { return { message: () => - `no valid output`, + `no valid output`, pass: true, }; }, @@ -78,7 +110,7 @@ describe('lexer', () => { const input = '[SingleTag]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'SingleTag', '0', '0'], + [TYPE.TAG, 'SingleTag', 0, 0, 0, 11], ]; expect(tokens).toBeMantchOutput(output); @@ -88,8 +120,8 @@ describe('lexer', () => { const input = '[user=111]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'user', '0', '0'], - [TYPE.ATTR_VALUE, '111', '0', '0'], + [TYPE.TAG, 'user', 0, 0, 0, 10], + [TYPE.ATTR_VALUE, '111', 6, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -99,10 +131,10 @@ describe('lexer', () => { const input = '[url=someval]GET[/url]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'url', '0', '0'], - [TYPE.ATTR_VALUE, 'someval', '0', '0'], - [TYPE.WORD, 'GET', '0', '0'], - [TYPE.TAG, '/url', '0', '0'], + [TYPE.TAG, 'url', 0, 0, 0, 13], + [TYPE.ATTR_VALUE, 'someval', 5, 0], + [TYPE.WORD, 'GET', 13, 0], + [TYPE.TAG, '/url', 17, 0, 16, 22], ]; expect(tokens).toBeMantchOutput(output); @@ -112,9 +144,9 @@ describe('lexer', () => { const input = '[ user=111]'; const tokens = tokenize(input); const output = [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'user=111]', '0', '0'], + [TYPE.WORD, '[', 0, 0, 0], + [TYPE.SPACE, ' ', 1, 0, 1], + [TYPE.WORD, 'user=111]', 2, 0, 2], ]; expect(tokens).toBeMantchOutput(output); @@ -125,7 +157,7 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'Single Tag', '0', '0'], + [TYPE.TAG, 'Single Tag', 0, 0, 0, 12], ]; expect(tokens).toBeMantchOutput(output); @@ -137,10 +169,10 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'textarea', '0', '0'], - [TYPE.ATTR_VALUE, 'disabled', '0', '0'], - [TYPE.WORD, 'world"', '0', '0'], - [TYPE.TAG, '/textarea', '0', '0'], + [TYPE.TAG, 'textarea', 0, 0], + [TYPE.ATTR_VALUE, 'disabled', 10, 0], + [TYPE.WORD, 'world"', 19, 0], + [TYPE.TAG, '/textarea', 25, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -153,32 +185,32 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'url', '0', '0'], - [TYPE.ATTR_NAME, 'href', '0', '0'], - [TYPE.ATTR_VALUE, '/groups/123/', '0', '0'], - [TYPE.ATTR_NAME, 'isNowrap', '0', '0'], - [TYPE.ATTR_VALUE, 'true', '0', '0'], - [TYPE.ATTR_NAME, 'isTextOverflow', '0', '0'], - [TYPE.ATTR_VALUE, 'true', '0', '0'], - [TYPE.ATTR_NAME, 'state', '0', '0'], - [TYPE.ATTR_VALUE, 'primary', '0', '0'], - [TYPE.NEW_LINE, '\n', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.TAG, 'avatar', '0', '0'], - [TYPE.ATTR_NAME, 'href', '0', '0'], - [TYPE.ATTR_VALUE, '/avatar/4/3/b/1606.jpg@20x20?cache=1561462725&bgclr=ffffff', '0', '0'], - [TYPE.ATTR_NAME, 'size', '0', '0'], - [TYPE.ATTR_VALUE, 'xs', '0', '0'], - [TYPE.TAG, '/avatar', '0', '0'], - [TYPE.NEW_LINE, '\n', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'Group', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'Name', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'Go', '0', '0'], - [TYPE.TAG, '/url', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], + [TYPE.TAG, 'url', 0, 0, 0, 73], + [TYPE.ATTR_NAME, 'href', 5, 0, 5], + [TYPE.ATTR_VALUE, '/groups/123/', 10, 0, 10], + [TYPE.ATTR_NAME, 'isNowrap', 25, 0, 25], + [TYPE.ATTR_VALUE, 'true', 34, 0, 34], + [TYPE.ATTR_NAME, 'isTextOverflow', 39, 0, 39], + [TYPE.ATTR_VALUE, 'true', 54, 0, 54], + [TYPE.ATTR_NAME, 'state', 59, 0, 59], + [TYPE.ATTR_VALUE, 'primary', 65, 0, 65], + [TYPE.NEW_LINE, '\n', 73, 0, 73], + [TYPE.SPACE, ' ', 0, 1, 74], + [TYPE.TAG, 'avatar', 8, 1, 82, 164], + [TYPE.ATTR_NAME, 'href', 16, 1, 90], + [TYPE.ATTR_VALUE, '/avatar/4/3/b/1606.jpg@20x20?cache=1561462725&bgclr=ffffff', 21, 1, 95], + [TYPE.ATTR_NAME, 'size', 82, 1, 156], + [TYPE.ATTR_VALUE, 'xs', 87, 1, 161], + [TYPE.TAG, '/avatar', 90, 1, 164, 173], + [TYPE.NEW_LINE, '\n', 100, 1, 173], + [TYPE.SPACE, ' ', 0, 2, 174], + [TYPE.WORD, 'Group', 9, 2, 184], + [TYPE.SPACE, ' ', 14, 2, 189], + [TYPE.WORD, 'Name', 15, 2, 190], + [TYPE.SPACE, ' ', 19, 2, 194], + [TYPE.WORD, 'Go', 20, 2, 195], + [TYPE.TAG, '/url', 22, 2, 196, 202], + [TYPE.SPACE, ' ', 28, 2, 203], ]; expect(tokens).toBeMantchOutput(output); @@ -189,15 +221,15 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.WORD, '"Someone', '0', '0'], - [TYPE.SPACE, ' ', '8', '0'], - [TYPE.WORD, 'Like', '8', '0'], - [TYPE.SPACE, ' ', '13', '0'], - [TYPE.WORD, 'You"', '13', '0'], - [TYPE.SPACE, ' ', '18', '0'], - [TYPE.WORD, 'by', '18', '0'], - [TYPE.SPACE, ' ', '21', '0'], - [TYPE.WORD, 'Adele', '21', '0'], + [TYPE.WORD, '"Someone', 0, 0], + [TYPE.SPACE, ' ', 8, 0], + [TYPE.WORD, 'Like', 9, 0], + [TYPE.SPACE, ' ', 13, 0], + [TYPE.WORD, 'You"', 14, 0], + [TYPE.SPACE, ' ', 18, 0], + [TYPE.WORD, 'by', 19, 0], + [TYPE.SPACE, ' ', 21, 0], + [TYPE.WORD, 'Adele', 22, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -208,13 +240,13 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.SPACE, ' ', '1', '0'], - [TYPE.TAG, 'h1', '2', '0'], - [TYPE.WORD, 'G', '1', '0'], - [TYPE.TAG, '/h1', '7', '0'], - [TYPE.SPACE, ' ', '12', '0'], - [TYPE.WORD, ']', '7', '0'], + [TYPE.WORD, '[', 0, 0], + [TYPE.SPACE, ' ', 1, 0], + [TYPE.TAG, 'h1', 2, 0, 2, 6], + [TYPE.WORD, 'G', 6, 0], + [TYPE.TAG, '/h1', 7, 0, 7, 12], + [TYPE.SPACE, ' ', 12, 0], + [TYPE.WORD, ']', 13, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -224,10 +256,10 @@ describe('lexer', () => { const input = '[color="#ff0000"]Text[/color]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'color', '0', '0'], - [TYPE.ATTR_VALUE, '#ff0000', '6', '0'], - [TYPE.WORD, 'Text', '17', '0'], - [TYPE.TAG, '/color', '21', '0'], + [TYPE.TAG, 'color', 0, 0, 0, 17], + [TYPE.ATTR_VALUE, '#ff0000', 7, 0], + [TYPE.WORD, 'Text', 17, 0], + [TYPE.TAG, '/color', 21, 0, 21, 29], ]; expect(tokens).toBeMantchOutput(output); @@ -237,13 +269,13 @@ describe('lexer', () => { const input = '[url text="Foo Bar" text2="Foo Bar 2"]Text[/url]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'url', '0', '0'], - [TYPE.ATTR_NAME, 'text', '4', '0'], - [TYPE.ATTR_VALUE, 'Foo Bar', '9', '0'], - [TYPE.ATTR_NAME, 'text2', '4', '0'], - [TYPE.ATTR_VALUE, 'Foo Bar 2', '9', '0'], - [TYPE.WORD, 'Text', '20', '0'], - [TYPE.TAG, '/url', '24', '0'], + [TYPE.TAG, 'url', 0, 0, 0, 38], + [TYPE.ATTR_NAME, 'text', 5, 0], + [TYPE.ATTR_VALUE, 'Foo Bar', 10, 0], + [TYPE.ATTR_NAME, 'text2', 20, 0], + [TYPE.ATTR_VALUE, 'Foo Bar 2', 26, 0], + [TYPE.WORD, 'Text', 38, 0], + [TYPE.TAG, '/url', 42, 0, 42, 48], ]; expect(tokens).toBeMantchOutput(output); @@ -253,11 +285,11 @@ describe('lexer', () => { const input = `[url text="Foo \\"Bar"]Text[/url]`; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'url', '0', '0'], - [TYPE.ATTR_NAME, 'text', '4', '0'], - [TYPE.ATTR_VALUE, 'Foo "Bar', '9', '0'], - [TYPE.WORD, 'Text', '22', '0'], - [TYPE.TAG, '/url', '26', '0'], + [TYPE.TAG, 'url', 0, 0, 0, 22], + [TYPE.ATTR_NAME, 'text', 5, 0], + [TYPE.ATTR_VALUE, 'Foo "Bar', 10, 0], + [TYPE.WORD, 'Text', 22, 0], + [TYPE.TAG, '/url', 26, 0, 26, 32], ]; expect(tokens).toBeMantchOutput(output); @@ -267,11 +299,11 @@ describe('lexer', () => { const input = '[style color=#ff0000]Text[/style]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'style', '0', '0'], - [TYPE.ATTR_NAME, 'color', '6', '0'], - [TYPE.ATTR_VALUE, '#ff0000', '12', '0'], - [TYPE.WORD, 'Text', '21', '0'], - [TYPE.TAG, '/style', '25', '0'], + [TYPE.TAG, 'style', 0, 0, 0, 21], + [TYPE.ATTR_NAME, 'color', 7, 0], + [TYPE.ATTR_VALUE, '#ff0000', 13, 0], + [TYPE.WORD, 'Text', 21, 0], + [TYPE.TAG, '/style', 26, 0, 25, 33], ]; expect(tokens).toBeMantchOutput(output); @@ -286,30 +318,30 @@ describe('lexer', () => { const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'list', '0', '0'], - [TYPE.NEW_LINE, '\n', '6', '0'], - [TYPE.SPACE, ' ', '0', '1'], - [TYPE.TAG, '*', '3', '1'], - [TYPE.SPACE, ' ', '6', '1'], - [TYPE.WORD, 'Item', '7', '1'], - [TYPE.SPACE, ' ', '11', '1'], - [TYPE.WORD, '1.', '11', '1'], - [TYPE.NEW_LINE, '\n', '14', '1'], - [TYPE.SPACE, ' ', '0', '2'], - [TYPE.TAG, '*', '3', '2'], - [TYPE.SPACE, ' ', '6', '2'], - [TYPE.WORD, 'Item', '14', '1'], - [TYPE.SPACE, ' ', '11', '2'], - [TYPE.WORD, '2.', '11', '2'], - [TYPE.NEW_LINE, '\n', '14', '2'], - [TYPE.SPACE, ' ', '0', '3'], - [TYPE.TAG, '*', '3', '3'], - [TYPE.SPACE, ' ', '6', '3'], - [TYPE.WORD, 'Item', '14', '2'], - [TYPE.SPACE, ' ', '11', '3'], - [TYPE.WORD, '3.', '11', '3'], - [TYPE.NEW_LINE, '\n', '14', '3'], - [TYPE.TAG, '/list', '0', '4'], + [TYPE.TAG, 'list', 0, 0, 0, 6], + [TYPE.NEW_LINE, '\n', 6, 0], + [TYPE.SPACE, ' ', 0, 1], + [TYPE.TAG, '*', 3, 1, 10, 13], + [TYPE.SPACE, ' ', 6, 1], + [TYPE.WORD, 'Item', 7, 1], + [TYPE.SPACE, ' ', 11, 1], + [TYPE.WORD, '1.', 12, 1], + [TYPE.NEW_LINE, '\n', 14, 1], + [TYPE.SPACE, ' ', 0, 2], + [TYPE.TAG, '*', 3, 2, 25, 28], + [TYPE.SPACE, ' ', 6, 2], + [TYPE.WORD, 'Item', 7, 2], + [TYPE.SPACE, ' ', 11, 2], + [TYPE.WORD, '2.', 12, 2], + [TYPE.NEW_LINE, '\n', 14, 2], + [TYPE.SPACE, ' ', 0, 3], + [TYPE.TAG, '*', 3, 3, 40, 43], + [TYPE.SPACE, ' ', 6, 3], + [TYPE.WORD, 'Item', 7, 3], + [TYPE.SPACE, ' ', 11, 3], + [TYPE.WORD, '3.', 12, 3], + [TYPE.NEW_LINE, '\n', 14, 3], + [TYPE.TAG, '/list', 0, 4, 52, 59], ]; expect(tokens).toBeMantchOutput(output); @@ -319,19 +351,19 @@ describe('lexer', () => { const input = '[mytag1 size="15"]Tag1[/mytag1][mytag2 size="16"]Tag2[/mytag2][mytag3]Tag3[/mytag3]'; const tokens = tokenize(input); const output = [ - [TYPE.TAG, 'mytag1', 0, 0], - [TYPE.ATTR_NAME, 'size', 0, 0], - [TYPE.ATTR_VALUE, '15', 0, 0], - [TYPE.WORD, 'Tag1', 0, 0], - [TYPE.TAG, '/mytag1', 0, 0], - [TYPE.TAG, 'mytag2', 0, 0], - [TYPE.ATTR_NAME, 'size', 0, 0], - [TYPE.ATTR_VALUE, '16', 0, 0], - [TYPE.WORD, 'Tag2', 0, 0], - [TYPE.TAG, '/mytag2', 0, 0], - [TYPE.TAG, 'mytag3', 0, 0], - [TYPE.WORD, 'Tag3', 0, 0], - [TYPE.TAG, '/mytag3', 0, 0], + [TYPE.TAG, 'mytag1', 0, 0, 0, 18], + [TYPE.ATTR_NAME, 'size', 8, 0], + [TYPE.ATTR_VALUE, '15', 13, 0], + [TYPE.WORD, 'Tag1', 18, 0], + [TYPE.TAG, '/mytag1', 22, 0, 22, 31], + [TYPE.TAG, 'mytag2', 31, 0, 31, 49], + [TYPE.ATTR_NAME, 'size', 39, 0], + [TYPE.ATTR_VALUE, '16', 44, 0], + [TYPE.WORD, 'Tag2', 49, 0], + [TYPE.TAG, '/mytag2', 53, 0, 53, 62], + [TYPE.TAG, 'mytag3', 62, 0, 62, 70], + [TYPE.WORD, 'Tag3', 70, 0], + [TYPE.TAG, '/mytag3', 74, 0, 74, 83], ]; expect(tokens).toBeMantchOutput(output); @@ -351,51 +383,50 @@ describe('lexer', () => { const asserts = [ [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, ']', '0', '0'] + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, ']', 1, 0] ], [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, '=]', '0', '0'] + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, '=]', 1, 0] ], [ - [TYPE.WORD, '!', '0', '0'], - [TYPE.WORD, '[', '1', '0'], - [TYPE.WORD, '](image.jpg)', '1', '0'], - // [TYPE.WORD, '', '1', '0'], + [TYPE.WORD, '!', 0, 0], + [TYPE.WORD, '[', 1, 0], + [TYPE.WORD, '](image.jpg)', 2, 0], ], [ - [TYPE.WORD, 'x', '0', '0'], - [TYPE.SPACE, ' ', '1', '0'], - [TYPE.WORD, 'html(', '1', '0'], - [TYPE.TAG, 'a. title', '7', '0'], - [TYPE.TAG, ', alt', '17', '0'], - [TYPE.TAG, ', classes', '24', '0'], - [TYPE.WORD, ')', '7', '0'], - [TYPE.SPACE, ' ', '36', '0'], - [TYPE.WORD, 'x', '36', '0'], + [TYPE.WORD, 'x', 0, 0], + [TYPE.SPACE, ' ', 1, 0], + [TYPE.WORD, 'html(', 2, 0], + [TYPE.TAG, 'a. title', 7, 0, 7, 17], + [TYPE.TAG, ', alt', 17, 0, 17, 24], + [TYPE.TAG, ', classes', 24, 0, 24, 35], + [TYPE.WORD, ')', 35, 0], + [TYPE.SPACE, ' ', 36, 0], + [TYPE.WORD, 'x', 37, 0], ], [ - [TYPE.TAG, '/y', '0', '0'] + [TYPE.TAG, '/y', 0, 0, 0, 4] ], [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'sc', '0', '0'] + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'sc', 1, 0] ], [ // [sc / - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'sc', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, '/', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.TAG, '/sc', '0', '0'] + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'sc', 1, 0], + [TYPE.SPACE, ' ', 3, 0], + [TYPE.WORD, '/', 4, 0], + [TYPE.SPACE, ' ', 5, 0], + [TYPE.TAG, '/sc', 6, 0, 6, 11] ], [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'sc', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'arg="val', '0', '0'], + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'sc', 1, 0], + [TYPE.SPACE, ' ', 3, 0], + [TYPE.WORD, 'arg="val', 4, 0], ] ]; @@ -411,14 +442,14 @@ describe('lexer', () => { const input = `[Finger Part A [Finger]`; const tokens = tokenize(input); const output = [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'Finger', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'Part', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'A', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.TAG, 'Finger', '0', '0'] + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'Finger', 1, 0], + [TYPE.SPACE, ' ', 7, 0], + [TYPE.WORD, 'Part', 8, 0], + [TYPE.SPACE, ' ', 12, 0], + [TYPE.WORD, 'A', 13, 0], + [TYPE.SPACE, ' ', 14, 0], + [TYPE.TAG, 'Finger', 15, 0, 15, 23] ]; expect(tokens).toBeMantchOutput(output); @@ -428,12 +459,12 @@ describe('lexer', () => { const input = '[Finger Part A'; const tokens = tokenize(input); const output = [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'Finger', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'Part', '0', '0'], - [TYPE.SPACE, ' ', '0', '0'], - [TYPE.WORD, 'A', '0', '0'], + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'Finger', 1, 0], + [TYPE.SPACE, ' ', 7, 0], + [TYPE.WORD, 'Part', 8, 0], + [TYPE.SPACE, ' ', 12, 0], + [TYPE.WORD, 'A', 13, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -444,11 +475,11 @@ describe('lexer', () => { const tokens = tokenizeEscape(input); const output = [ - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'b', '0', '0'], - [TYPE.WORD, ']', '0', '0'], - [TYPE.WORD, 'test', '0', '0'], - [TYPE.WORD, '[', '0', '0'], + [TYPE.WORD, '[', 0, 0], + [TYPE.WORD, 'b', 2, 0], + [TYPE.WORD, ']', 3, 0], + [TYPE.WORD, 'test', 5, 0], + [TYPE.WORD, '[', 9, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -458,67 +489,67 @@ describe('lexer', () => { const input = '\\\\\\[b\\\\\\]test\\\\\\[/b\\\\\\]'; const tokens = tokenizeEscape(input); const output = [ - [TYPE.WORD, '\\', '0', '0'], - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, 'b', '0', '0'], - [TYPE.WORD, '\\', '0', '0'], - [TYPE.WORD, ']', '0', '0'], - [TYPE.WORD, 'test', '0', '0'], - [TYPE.WORD, '\\', '0', '0'], - [TYPE.WORD, '[', '0', '0'], - [TYPE.WORD, '/b', '0', '0'], - [TYPE.WORD, '\\', '0', '0'], - [TYPE.WORD, ']', '0', '0'], + [TYPE.WORD, '\\', 0, 0], + [TYPE.WORD, '[', 2, 0], + [TYPE.WORD, 'b', 4, 0], + [TYPE.WORD, '\\', 5, 0], + [TYPE.WORD, ']', 7, 0], + [TYPE.WORD, 'test', 9, 0], + [TYPE.WORD, '\\', 13, 0], + [TYPE.WORD, '[', 15, 0], + [TYPE.WORD, '/b', 17, 0], + [TYPE.WORD, '\\', 19, 0], + [TYPE.WORD, ']', 21, 0], ]; expect(tokens).toBeMantchOutput(output); }); test('context free tag [code]', () => { - const input = '[code] [b]some string[/b][/code]' + const input = '[code] [b]some string[/b][/code]'; const tokens = tokenizeContextFreeTags(input, ['code']); const output = [ - [TYPE.TAG, 'code', 0, 0], - [TYPE.SPACE, ' ', 0, 0], - [TYPE.WORD, '[', 0, 0], - [TYPE.WORD, 'b]some', 0, 0], - [TYPE.SPACE, ' ', 0, 0], - [TYPE.WORD, 'string', 0, 0], - [TYPE.WORD, '[', 0, 0], - [TYPE.WORD, '/b]', 0, 0], - [TYPE.TAG, '/code', 0, 0], - ] + [TYPE.TAG, 'code', 0, 0, 0, 6], + [TYPE.SPACE, ' ', 6, 0], + [TYPE.WORD, '[', 7, 0], + [TYPE.WORD, 'b]some', 8, 0], + [TYPE.SPACE, ' ', 14, 0], + [TYPE.WORD, 'string', 15, 0], + [TYPE.WORD, '[', 21, 0], + [TYPE.WORD, '/b]', 22, 0], + [TYPE.TAG, '/code', 25, 0, 25, 32], + ]; expect(tokens).toBeMantchOutput(output); - }) + }); test('context free tag case insensitive [CODE]', () => { - const input = '[CODE] [b]some string[/b][/CODE]' + const input = '[CODE] [b]some string[/b][/CODE]'; const tokens = tokenizeContextFreeTags(input, ['code']); const output = [ - [TYPE.TAG, 'CODE', 0, 0], - [TYPE.SPACE, ' ', 0, 0], - [TYPE.WORD, '[', 0, 0], - [TYPE.WORD, 'b]some', 0, 0], - [TYPE.SPACE, ' ', 0, 0], - [TYPE.WORD, 'string', 0, 0], - [TYPE.WORD, '[', 0, 0], - [TYPE.WORD, '/b]', 0, 0], - [TYPE.TAG, '/CODE', 0, 0], - ] + [TYPE.TAG, 'CODE', 0, 0, 0, 6], + [TYPE.SPACE, ' ', 6, 0], + [TYPE.WORD, '[', 7, 0], + [TYPE.WORD, 'b]some', 8, 0], + [TYPE.SPACE, ' ', 14, 0], + [TYPE.WORD, 'string', 15, 0], + [TYPE.WORD, '[', 21, 0], + [TYPE.WORD, '/b]', 22, 0], + [TYPE.TAG, '/CODE', 25, 0, 25, 32], + ]; expect(tokens).toBeMantchOutput(output); - }) + }); test('bad closed tag with escaped backslash', () => { const input = `[b]test[\\b]`; const tokens = tokenizeEscape(input); const output = [ - [TYPE.TAG, 'b', '0', '3'], - [TYPE.WORD, 'test', '0', '7'], - [TYPE.WORD, '[', '0', '8'], - [TYPE.WORD, '\\', '0', '9'], - [TYPE.WORD, 'b]', '0', '11'], + [TYPE.TAG, 'b', 0, 0, 0, 3], + [TYPE.WORD, 'test', 3, 0], + [TYPE.WORD, '[', 7, 0], + [TYPE.WORD, '\\', 8, 0], + [TYPE.WORD, 'b]', 9, 0], ]; expect(tokens).toBeMantchOutput(output); @@ -531,17 +562,17 @@ describe('lexer', () => { const content = ``; const tokens = tokenizeHTML(content); const output = [ - [TYPE.TAG, 'button', 2, 0], - [TYPE.ATTR_NAME, 'id', 2, 0], - [TYPE.ATTR_VALUE, 'test0', 2, 0], - [TYPE.ATTR_NAME, 'class', 2, 0], - [TYPE.ATTR_VALUE, 'value0', 2, 0], - [TYPE.ATTR_NAME, 'title', 2, 0], - [TYPE.ATTR_VALUE, 'value1', 2, 0], - [TYPE.WORD, "class=\"value0\"", 2, 0], - [TYPE.SPACE, " ", 2, 0], - [TYPE.WORD, "title=\"value1\"", 2, 0], - [TYPE.TAG, '/button', 2, 0] + [TYPE.TAG, 'button', 0, 0, 0, 49], + [TYPE.ATTR_NAME, 'id', 8, 0], + [TYPE.ATTR_VALUE, 'test0', 11, 0], + [TYPE.ATTR_NAME, 'class', 19, 0], + [TYPE.ATTR_VALUE, 'value0', 25, 0], + [TYPE.ATTR_NAME, 'title', 34, 0], + [TYPE.ATTR_VALUE, 'value1', 40, 0], + [TYPE.WORD, "class=\"value0\"", 49, 0], + [TYPE.SPACE, " ", 63, 0], + [TYPE.WORD, "title=\"value1\"", 64, 0], + [TYPE.TAG, '/button', 78, 0, 78, 87] ]; expect(tokens).toBeMantchOutput(output); @@ -551,16 +582,16 @@ describe('lexer', () => { const content = ``; const tokens = tokenizeHTML(content); const output = [ - [TYPE.TAG, 'button', 2, 0], - [TYPE.ATTR_NAME, 'id', 2, 0], - [TYPE.ATTR_VALUE, 'test1', 2, 0], - [TYPE.ATTR_NAME, 'class', 2, 0], - [TYPE.ATTR_VALUE, 'value2', 2, 0], - [TYPE.ATTR_VALUE, 'disabled', 2, 0], - [TYPE.WORD, "class=value2", 2, 0], - [TYPE.SPACE, " ", 2, 0], - [TYPE.WORD, "disabled", 2, 0], - [TYPE.TAG, '/button', 2, 0] + [TYPE.TAG, 'button', 0, 0, 0, 41], + [TYPE.ATTR_NAME, 'id', 8, 0], + [TYPE.ATTR_VALUE, 'test1', 11, 0], + [TYPE.ATTR_NAME, 'class', 19, 0], + [TYPE.ATTR_VALUE, 'value2', 25, 0], + [TYPE.ATTR_VALUE, 'disabled', 32, 0], + [TYPE.WORD, "class=value2", 41, 0], + [TYPE.SPACE, " ", 54, 0], + [TYPE.WORD, "disabled", 55, 0], + [TYPE.TAG, '/button', 63, 0, 62, 71] ]; expect(tokens).toBeMantchOutput(output); @@ -570,15 +601,15 @@ describe('lexer', () => { const content = ``; const tokens = tokenizeHTML(content); const output = [ - [TYPE.TAG, 'button', 2, 0], - [TYPE.ATTR_NAME, 'id', 2, 0], - [TYPE.ATTR_VALUE, 'test2', 2, 0], - [TYPE.ATTR_NAME, 'class', 2, 0], - [TYPE.ATTR_VALUE, 'value4', 2, 0], - [TYPE.ATTR_NAME, 'title', 2, 0], - [TYPE.ATTR_VALUE, 'value5', 2, 0], - [TYPE.WORD, "class=\"value4\"title=\"value5\"", 2, 0], - [TYPE.TAG, '/button', 2, 0] + [TYPE.TAG, 'button', 0, 0, 0, 48], + [TYPE.ATTR_NAME, 'id', 8, 0], + [TYPE.ATTR_VALUE, 'test2', 11, 0], + [TYPE.ATTR_NAME, 'class', 19, 0], + [TYPE.ATTR_VALUE, 'value4', 25, 0], + [TYPE.ATTR_NAME, 'title', 34, 0], + [TYPE.ATTR_VALUE, 'value5', 39, 0], + [TYPE.WORD, "class=\"value4\"title=\"value5\"", 48, 0], + [TYPE.TAG, '/button', 76, 0, 76, 85] ]; expect(tokens).toBeMantchOutput(output); @@ -601,7 +632,7 @@ input{padding:0px;margin:0px;font-size:9pt} input.medium{width:100px;height:18px} input.buttonred{cursor:hand;font-family:verdana;background:#d12124;color:#fff;height:1.4em;font-weight:bold;font-size:9pt;padding:0px 2px;margin:0px;border:0px none #000} --> -` +`; const tokens = tokenizeHTML(content); expect(tokens).toBeMantchOutput([]); }); @@ -615,6 +646,6 @@ input.buttonred{cursor:hand;font-family:verdana;background:#d12124;color:#fff;he `; const tokens = tokenizeHTML(content); expect(tokens).toBeMantchOutput([]); - }) - }) + }); + }); }); diff --git a/packages/bbob-parser/test/parse.test.ts b/packages/bbob-parser/test/parse.test.ts index 69c67422..c6afe008 100644 --- a/packages/bbob-parser/test/parse.test.ts +++ b/packages/bbob-parser/test/parse.test.ts @@ -1,10 +1,10 @@ -import { parse } from '../src' -import type { TagNodeTree } from "@bbob/plugin-helper"; +import { parse } from '../src'; +import type { TagNode, TagNodeTree } from "@bbob/types"; describe('Parser', () => { const expectOutput = (ast: TagNodeTree, output: Partial) => { expect(ast).toBeInstanceOf(Array); - expect(ast).toEqual(output); + expect(ast).toMatchObject(output as {} | TagNode[]); }; test('parse paired tags tokens', () => { @@ -20,6 +20,14 @@ describe('Parser', () => { ' ', 'Bar', ], + startTagPos: { + start: 0, + end: 17, + }, + endTagPos: { + start: 24, + end: 31, + }, }, ]; @@ -37,6 +45,14 @@ describe('Parser', () => { ' ', 'Bar', ], + startTagPos: { + start: 0, + end: 5, + }, + endTagPos: { + start: 12, + end: 18, + }, }, ]; @@ -60,6 +76,14 @@ describe('Parser', () => { '[Bar]', ' ' ], + startTagPos: { + start: 0, + end: 15, + }, + endTagPos: { + start: 25, + end: 30, + }, }, ]; @@ -78,7 +102,7 @@ describe('Parser', () => { '[blah foo="bar"]', 'world', '[/blah]' - ]) + ]); }); test('parse only allowed tags with named param', () => { @@ -93,7 +117,7 @@ describe('Parser', () => { '[blah="bar"]', 'world', '[/blah]' - ]) + ]); }); test('parse only allowed tags inside disabled tags', () => { @@ -107,6 +131,14 @@ describe('Parser', () => { tag: 'ch', attrs: {}, content: ['E'], + startTagPos: { + start: 7, + end: 11, + }, + endTagPos: { + start: 12, + end: 17, + }, }, '\n', 'A', @@ -126,6 +158,14 @@ describe('Parser', () => { tag: 'ch', attrs: {}, content: ['A'], + startTagPos: { + start: 81, + end: 85, + }, + endTagPos: { + start: 86, + end: 91, + }, }, '\n', 'All', @@ -159,12 +199,20 @@ describe('Parser', () => { '[Bar]', ' ' ], + startTagPos: { + start: 0, + end: 15, + }, + endTagPos: { + start: 25, + end: 30, + }, }, ]; expectOutput(ast, output); }); - }) + }); describe('contextFreeTags', () => { test('context free tag [code]', () => { @@ -176,20 +224,28 @@ describe('Parser', () => { tag: 'code', attrs: {}, content: [ - ' ', - '[', - 'b]some', - ' ', - 'string', - '[', - '/b]' - ] + ' ', + '[', + 'b]some', + ' ', + 'string', + '[', + '/b]' + ], + startTagPos: { + start: 0, + end: 6, + }, + endTagPos: { + start: 25, + end: 32, + }, } - ] + ]; expectOutput(ast, output); - }) - }) + }); + }); test('parse inconsistent tags', () => { const ast = parse('[h1 name=value]Foo [Bar] /h1]'); @@ -199,14 +255,22 @@ describe('Parser', () => { name: 'value' }, tag: 'h1', - content: [] + content: [], + startTagPos: { + start: 0, + end: 15, + }, }, 'Foo', ' ', { tag: 'bar', attrs: {}, - content: [] + content: [], + startTagPos: { + start: 19, + end: 24, + }, }, ' ', '/h1]', @@ -224,6 +288,14 @@ describe('Parser', () => { 'https://github.com/jilizart/bbob': 'https://github.com/jilizart/bbob', }, content: ['BBob'], + startTagPos: { + start: 0, + end: 38, + }, + endTagPos: { + start: 42, + end: 48, + }, }, ]; @@ -241,6 +313,14 @@ describe('Parser', () => { text: 'Foo Bar', }, content: ['Text'], + startTagPos: { + start: 0, + end: 64, + }, + endTagPos: { + start: 68, + end: 74, + }, }, ]; @@ -256,6 +336,10 @@ describe('Parser', () => { 'https://github.com/jilizart/bbob': 'https://github.com/jilizart/bbob', }, content: [], + startTagPos: { + start: 0, + end: 38, + }, }, ]; @@ -279,6 +363,14 @@ describe('Parser', () => { size: '15', }, content: ['Tag1'], + startTagPos: { + start: 0, + end: 18, + }, + endTagPos: { + start: 22, + end: 31, + }, }, { tag: 'mytag2', @@ -286,11 +378,27 @@ describe('Parser', () => { size: '16', }, content: ['Tag2'], + startTagPos: { + start: 31, + end: 49, + }, + endTagPos: { + start: 53, + end: 62, + }, }, { tag: 'mytag3', attrs: {}, content: ['Tag3'], + startTagPos: { + start: 62, + end: 70, + }, + endTagPos: { + start: 74, + end: 83, + }, }, ]; @@ -306,6 +414,14 @@ describe('Parser', () => { tag: 'b', attrs: {}, content: ['hello'], + startTagPos: { + start: 0, + end: 17, + }, + endTagPos: { + start: 24, + end: 31, + }, }, ' ', { @@ -314,6 +430,14 @@ describe('Parser', () => { disabled: 'disabled', }, content: ['world'], + startTagPos: { + start: 0, + end: 17, + }, + endTagPos: { + start: 24, + end: 31, + }, }, ]); }); @@ -328,10 +452,120 @@ describe('Parser', () => { 'https://github.com/JiLiZART/bbob/search?q=any&unscoped_q=any': 'https://github.com/JiLiZART/bbob/search?q=any&unscoped_q=any', }, content: ['GET'], + startTagPos: { + start: 0, + end: 66, + }, + endTagPos: { + start: 69, + end: 75, + }, }, ]); }); + test('parse triple nested tags', () => { + const ast = parse(`this is outside [spoiler title="name with + multiline + attr value"] this is a spoiler + [b]this is bold [i]this is bold and italic[/i] this is bold again[/b] + [/spoiler]this is outside again`); + expectOutput(ast, [ + "this", + " ", + "is", + " ", + "outside", + " ", + { + attrs: { + "title": "name with\n multiline\n attr value", + }, + content: [ + " ", + "this", + " ", + "is", + " ", + "a", + " ", + "spoiler", + "\n", + " ", + { + attrs: {}, + content: [ + "this", + " ", + "is", + " ", + "bold", + " ", + { + attrs: {}, + content: [ + "this", + " ", + "is", + " ", + "bold", + " ", + "and", + " ", + "italic", + ], + endTagPos: { + "end": 147, + "start": 143, + }, + startTagPos: { + "end": 120, + "start": 117, + }, + "tag": "i", + }, + " ", + "this", + " ", + "is", + " ", + "bold", + " ", + "again", + ], + endTagPos: { + "end": 170, + "start": 166, + }, + startTagPos: { + "end": 104, + "start": 101, + }, + tag: "b", + }, + "\n", + " ", + ], + endTagPos: { + "end": 187, + "start": 177, + }, + startTagPos: { + "end": 76, + "start": 16, + }, + tag: "spoiler", + }, + "this", + " ", + "is", + " ", + "outside", + " ", + "again", + ]); + }); + test('parse tag with camelCase params', () => { const ast = parse(`[url href="/groups/123/" isNowrap=true isTextOverflow=true state=primary] [avatar href="/avatar/4/3/b/1606.jpg@20x20?cache=1561462725&bgclr=ffffff" size=xs][/avatar] @@ -355,7 +589,15 @@ describe('Parser', () => { href: '/avatar/4/3/b/1606.jpg@20x20?cache=1561462725&bgclr=ffffff', size: 'xs' }, - content: [] + content: [], + startTagPos: { + start: 82, + end: 164, + }, + endTagPos: { + start: 164, + end: 173, + }, }, '\n', ' ', @@ -365,6 +607,14 @@ describe('Parser', () => { ' ', 'Go', ], + startTagPos: { + start: 0, + end: 73, + }, + endTagPos: { + start: 196, + end: 202, + }, }, ' ', ]); @@ -380,6 +630,14 @@ describe('Parser', () => { href: 'https://docs.google.com/spreadsheets/d/1W9VPUESF_NkbSa_HtRFrQNl0nYo8vPCxJFy7jD3Tpio/edit#gid=0', }, content: ['Docs'], + startTagPos: { + start: 0, + end: 105, + }, + endTagPos: { + start: 109, + end: 115, + }, }, ]); }); @@ -389,56 +647,146 @@ describe('Parser', () => { [quote]xxxsdfasdf sdfasdfasdf -[url=xxx]xxx[/url]` +[url=xxx]xxx[/url]`; expectOutput( - parse(str), - [ - { tag: 'quote', attrs: {}, content: ['some'] }, - { tag: 'color', attrs: { red: 'red' }, content: ['test'] }, - '\n', - '[quote]', - 'xxxsdfasdf', - '\n', - 'sdfasdfasdf', - '\n', - '\n', - { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] } - ] - ) - }) + parse(str), + [ + { + tag: 'quote', attrs: {}, content: ['some'], + startTagPos: { + start: 0, + end: 7, + }, + endTagPos: { + start: 11, + end: 19, + }, + }, + { + tag: 'color', attrs: { red: 'red' }, content: ['test'], + startTagPos: { + start: 19, + end: 30, + }, + endTagPos: { + start: 34, + end: 42, + }, + }, + '\n', + '[quote]', + 'xxxsdfasdf', + '\n', + 'sdfasdfasdf', + '\n', + '\n', + { + tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], + startTagPos: { + start: 74, + end: 83, + }, + endTagPos: { + start: 86, + end: 92, + }, + } + ] + ); + }); test('parse with lost closing tag on start', () => { - const str = `[quote]xxxsdfasdf[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url]` + const str = `[quote]xxxsdfasdf[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url]`; expectOutput( - parse(str), - [ - '[quote]', - 'xxxsdfasdf', - { tag: 'quote', attrs: {}, content: ['some'] }, - { tag: 'color', attrs: { red: 'red' }, content: ['test'] }, - 'sdfasdfasdf', - { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] } - ] - ) - }) + parse(str), + [ + '[quote]', + 'xxxsdfasdf', + { + tag: 'quote', attrs: {}, content: ['some'], + startTagPos: { + start: 17, + end: 24, + }, + endTagPos: { + start: 28, + end: 36, + }, + }, + { + tag: 'color', attrs: { red: 'red' }, content: ['test'], + startTagPos: { + start: 36, + end: 47, + }, + endTagPos: { + start: 51, + end: 59, + }, + }, + 'sdfasdfasdf', + { + tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], + startTagPos: { + start: 70, + end: 79, + }, + endTagPos: { + start: 82, + end: 88, + }, + } + ] + ); + }); test('parse with lost closing tag on end', () => { - const str = `[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url][quote]xxxsdfasdf` + const str = `[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url][quote]xxxsdfasdf`; expectOutput( - parse(str), - [ - { tag: 'quote', attrs: {}, content: ['some'] }, - { tag: 'color', attrs: { red: 'red' }, content: ['test'] }, - 'sdfasdfasdf', - { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'] }, - '[quote]', - 'xxxsdfasdf', - ] - ) - }) + parse(str), + [ + { + tag: 'quote', attrs: {}, content: ['some'], + startTagPos: { + start: 0, + end: 7, + }, + endTagPos: { + start: 11, + end: 19, + }, + }, + { + tag: 'color', attrs: { red: 'red' }, content: ['test'], + startTagPos: { + start: 19, + end: 30, + }, + endTagPos: { + start: 34, + end: 42, + }, + }, + 'sdfasdfasdf', + { + tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], + startTagPos: { + start: 53, + end: 62, + }, + endTagPos: { + start: 65, + end: 71, + }, + }, + '[quote]', + 'xxxsdfasdf', + ] + ); + }); describe('html', () => { const parseHTML = (input: string) => parse(input, { openTag: '<', closeTag: '>' }); @@ -459,7 +807,15 @@ sdfasdfasdf "class=\"value0\"", " ", "title=\"value1\"" - ] + ], + startTagPos: { + start: 0, + end: 49, + }, + endTagPos: { + start: 78, + end: 87, + }, } ]); }); @@ -481,7 +837,15 @@ sdfasdfasdf "class=value2", " ", "disabled" - ] + ], + startTagPos: { + start: 0, + end: 50, + }, + endTagPos: { + start: 71, + end: 80, + }, } ]); }); @@ -500,7 +864,15 @@ sdfasdfasdf }, "content": [ "class=\"value4\"title=\"value5\"" - ] + ], + startTagPos: { + start: 0, + end: 48, + }, + endTagPos: { + start: 76, + end: 85, + }, } ]); }); diff --git a/packages/bbob-plugin-helper/src/TagNode.ts b/packages/bbob-plugin-helper/src/TagNode.ts index 8f792832..8bb6192c 100644 --- a/packages/bbob-plugin-helper/src/TagNode.ts +++ b/packages/bbob-plugin-helper/src/TagNode.ts @@ -30,38 +30,40 @@ const getTagAttrs = (tag: string, params: Record) 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((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 implements TagNodeObject { - public readonly tag: string | TagValue - public attrs: Record - public content: TagNodeTree + public readonly tag: string | TagValue; + public attrs: Record; + public content: TagNodeTree; + public startTagPos?: { start: number; end: number; }; + public endTagPos?: { start: number; end: number; }; constructor(tag: string | TagValue, attrs: Record, 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 implements TagNodeObject { return appendToNode(this, value); } + setStartTagPos(start: number, end: number) { + this.startTagPos = { start, end }; + } + + setEndTagPos(start: number, end: number) { + this.endTagPos = { start, end }; + } + get length(): number { return getNodeLength(this); } @@ -91,14 +101,21 @@ export class TagNode 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.startTagPos) { + newNode.setStartTagPos(this.startTagPos.start, this.startTagPos.end); + } + if (this.endTagPos) { + newNode.setEndTagPos(this.endTagPos.start, this.endTagPos.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; } @@ -106,10 +123,10 @@ export class TagNode implements TagNodeObject { } static create(tag: string, attrs: Record = {}, content: TagNodeTree = null) { - return new TagNode(tag, attrs, content) + return new TagNode(tag, attrs, content); } static isOf(node: TagNode, type: string) { - return (node.tag === type) + return (node.tag === type); } } diff --git a/packages/bbob-types/src/common.ts b/packages/bbob-types/src/common.ts index 42eb4dac..4ca745c1 100644 --- a/packages/bbob-types/src/common.ts +++ b/packages/bbob-types/src/common.ts @@ -1,13 +1,15 @@ -export type StringNode = string | number +export type StringNode = string | number; export interface TagNodeObject { - readonly tag: TagValue - attrs?: Record - content?: TagNodeTree + readonly tag: TagValue; + attrs?: Record; + content?: TagNodeTree; + startTagPos?: { start: number; end: number; }; + endTagPos?: { start: number; end: number; }; } -export type NodeContent = TagNodeObject | StringNode | null +export type NodeContent = TagNodeObject | StringNode | null; -export type PartialNodeContent = Partial> | StringNode | null +export type PartialNodeContent = Partial> | StringNode | null; -export type TagNodeTree = NodeContent | NodeContent[] | null +export type TagNodeTree = NodeContent | NodeContent[] | null; diff --git a/packages/bbob-types/src/parser.ts b/packages/bbob-types/src/parser.ts index 55792e28..3ac7736f 100644 --- a/packages/bbob-types/src/parser.ts +++ b/packages/bbob-types/src/parser.ts @@ -9,7 +9,15 @@ export interface ParseError { export interface TagNode { readonly tag: string attrs?: Record - content?: TagNodeTree + content?: TagNodeTree, + startTagPos?: { + start: number; + end: number; + }; + endTagPos?: { + start: number; + end: number; + }; } export interface Token { From da2f325a82cd62fd6ace28b2dfd138356c60a15b Mon Sep 17 00:00:00 2001 From: Alteras1 <42795314+Alteras1@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:02:58 -0700 Subject: [PATCH 2/3] Simplify language for TagNode and Token --- packages/bbob-core/test/index.test.ts | 42 +-- packages/bbob-parser/src/Token.ts | 4 +- packages/bbob-parser/src/parse.ts | 4 +- packages/bbob-parser/test/Token.test.ts | 4 +- packages/bbob-parser/test/parse.test.ts | 418 ++++++++++----------- packages/bbob-plugin-helper/src/TagNode.ts | 20 +- packages/bbob-types/src/common.ts | 4 +- packages/bbob-types/src/parser.ts | 12 +- 8 files changed, 254 insertions(+), 254 deletions(-) diff --git a/packages/bbob-core/test/index.test.ts b/packages/bbob-core/test/index.test.ts index b2852bfa..3556d611 100644 --- a/packages/bbob-core/test/index.test.ts +++ b/packages/bbob-core/test/index.test.ts @@ -11,15 +11,15 @@ describe('@bbob/core', () => { const res = process([], '[style size="15px"]Large Text[/style]'); const ast = res.tree; - expect(res.html).toBe('[{"tag":"style","attrs":{"size":"15px"},"content":["Large"," ","Text"],"startTagPos":{"start":0,"end":19},"endTagPos":{"start":29,"end":37}}]'); + expect(res.html).toBe('[{"tag":"style","attrs":{"size":"15px"},"content":["Large"," ","Text"],"start":{"from":0,"to":19},"end":{"from":29,"to":37}}]'); expect(ast).toBeInstanceOf(Array); expect(stringify(ast)).toEqual(stringify([ { tag: 'style', attrs: { size: '15px' }, content: ["Large", " ", "Text"], - startTagPos: { start: 0, end: 19 }, - endTagPos: { start: 29, end: 37 }, + start: { from: 0, to: 19 }, + end: { from: 29, to: 37 }, } ])); }); @@ -64,13 +64,13 @@ describe('@bbob/core', () => { 'Text', 'Test' ], - startTagPos: { - start: 0, - end: 19 + start: { + from: 0, + to: 19 }, - endTagPos: { - start: 29, - end: 37 + end: { + from: 29, + to: 37 } } ])); @@ -110,13 +110,13 @@ describe('@bbob/core', () => { content: [], } ], - startTagPos: { - start: 0, - end: 7 + start: { + from: 0, + to: 7 }, - endTagPos: { - start: 20, - end: 28 + end: { + from: 20, + to: 28 } } ])); @@ -151,8 +151,8 @@ describe('@bbob/core', () => { content: [ 'Tag1' ], - startTagPos: { start: 0, end: 18 }, - endTagPos: { start: 22, end: 31 } + start: { from: 0, to: 18 }, + end: { from: 22, to: 31 } }, { tag: 'mytag2', @@ -163,8 +163,8 @@ describe('@bbob/core', () => { content: [ 'Tag2' ], - startTagPos: { start: 31, end: 49 }, - endTagPos: { start: 53, end: 62 } + start: { from: 31, to: 49 }, + end: { from: 53, to: 62 } }, { tag: 'mytag3', @@ -172,8 +172,8 @@ describe('@bbob/core', () => { content: [ 'Tag3' ], - startTagPos: { start: 62, end: 70 }, - endTagPos: { start: 74, end: 83 } + start: { from: 62, to: 70 }, + end: { from: 74, to: 83 } } ])); }); diff --git a/packages/bbob-parser/src/Token.ts b/packages/bbob-parser/src/Token.ts index 8da06d7c..fbc9af19 100644 --- a/packages/bbob-parser/src/Token.ts +++ b/packages/bbob-parser/src/Token.ts @@ -159,11 +159,11 @@ class Token implements TokenInterface { return getTokenColumn(this); } - getStartPos() { + getStart() { return getStartPosition(this); } - getEndPos() { + getEnd() { return getEndPosition(this); } diff --git a/packages/bbob-parser/src/parse.ts b/packages/bbob-parser/src/parse.ts index bceea1bd..8e15baf7 100644 --- a/packages/bbob-parser/src/parse.ts +++ b/packages/bbob-parser/src/parse.ts @@ -186,7 +186,7 @@ function parse(input: string, opts: ParseOptions = {}) { flushTagNodes(); const tagNode = TagNode.create(token.getValue(), {}, []); - tagNode.setStartTagPos(token.getStartPos(), token.getEndPos()); + tagNode.setStart(token.getStart(), token.getEnd()); const isNested = isTokenNested(token); tagNodes.push(tagNode); @@ -206,7 +206,7 @@ function parse(input: string, opts: ParseOptions = {}) { function handleTagEnd(token: Token) { const lastTagNode = nestedNodes.last(); if (isTagNode(lastTagNode)) { - lastTagNode.setEndTagPos(token.getStartPos(), token.getEndPos()); + lastTagNode.setEnd(token.getStart(), token.getEnd()); } flushTagNodes(); diff --git a/packages/bbob-parser/test/Token.test.ts b/packages/bbob-parser/test/Token.test.ts index 05162214..89774e4c 100644 --- a/packages/bbob-parser/test/Token.test.ts +++ b/packages/bbob-parser/test/Token.test.ts @@ -59,12 +59,12 @@ describe('Token', () => { test('getStartPos', () => { const token = new Token(TYPE_TAG, 'my-tag', 12, 14, 50); - expect(token.getStartPos()).toBe(50); + expect(token.getStart()).toBe(50); }); test('getEndPos', () => { const token = new Token(TYPE_TAG, 'my-tag', 12, 14, 50, 60); - expect(token.getEndPos()).toBe(60); + expect(token.getEnd()).toBe(60); }); test('toString', () => { const tokenEnd = new Token(TYPE_TAG, '/my-tag', 12, 14); diff --git a/packages/bbob-parser/test/parse.test.ts b/packages/bbob-parser/test/parse.test.ts index c6afe008..f2dbb162 100644 --- a/packages/bbob-parser/test/parse.test.ts +++ b/packages/bbob-parser/test/parse.test.ts @@ -20,13 +20,13 @@ describe('Parser', () => { ' ', 'Bar', ], - startTagPos: { - start: 0, - end: 17, + start: { + from: 0, + to: 17, }, - endTagPos: { - start: 24, - end: 31, + end: { + from: 24, + to: 31, }, }, ]; @@ -45,13 +45,13 @@ describe('Parser', () => { ' ', 'Bar', ], - startTagPos: { - start: 0, - end: 5, + start: { + from: 0, + to: 5, }, - endTagPos: { - start: 12, - end: 18, + end: { + from: 12, + to: 18, }, }, ]; @@ -76,13 +76,13 @@ describe('Parser', () => { '[Bar]', ' ' ], - startTagPos: { - start: 0, - end: 15, + start: { + from: 0, + to: 15, }, - endTagPos: { - start: 25, - end: 30, + end: { + from: 25, + to: 30, }, }, ]; @@ -131,13 +131,13 @@ describe('Parser', () => { tag: 'ch', attrs: {}, content: ['E'], - startTagPos: { - start: 7, - end: 11, + start: { + from: 7, + to: 11, }, - endTagPos: { - start: 12, - end: 17, + end: { + from: 12, + to: 17, }, }, '\n', @@ -158,13 +158,13 @@ describe('Parser', () => { tag: 'ch', attrs: {}, content: ['A'], - startTagPos: { - start: 81, - end: 85, + start: { + from: 81, + to: 85, }, - endTagPos: { - start: 86, - end: 91, + end: { + from: 86, + to: 91, }, }, '\n', @@ -199,13 +199,13 @@ describe('Parser', () => { '[Bar]', ' ' ], - startTagPos: { - start: 0, - end: 15, + start: { + from: 0, + to: 15, }, - endTagPos: { - start: 25, - end: 30, + end: { + from: 25, + to: 30, }, }, ]; @@ -232,13 +232,13 @@ describe('Parser', () => { '[', '/b]' ], - startTagPos: { - start: 0, - end: 6, + start: { + from: 0, + to: 6, }, - endTagPos: { - start: 25, - end: 32, + end: { + from: 25, + to: 32, }, } ]; @@ -256,9 +256,9 @@ describe('Parser', () => { }, tag: 'h1', content: [], - startTagPos: { - start: 0, - end: 15, + start: { + from: 0, + to: 15, }, }, 'Foo', @@ -267,9 +267,9 @@ describe('Parser', () => { tag: 'bar', attrs: {}, content: [], - startTagPos: { - start: 19, - end: 24, + start: { + from: 19, + to: 24, }, }, ' ', @@ -288,13 +288,13 @@ describe('Parser', () => { 'https://github.com/jilizart/bbob': 'https://github.com/jilizart/bbob', }, content: ['BBob'], - startTagPos: { - start: 0, - end: 38, + start: { + from: 0, + to: 38, }, - endTagPos: { - start: 42, - end: 48, + end: { + from: 42, + to: 48, }, }, ]; @@ -313,13 +313,13 @@ describe('Parser', () => { text: 'Foo Bar', }, content: ['Text'], - startTagPos: { - start: 0, - end: 64, + start: { + from: 0, + to: 64, }, - endTagPos: { - start: 68, - end: 74, + end: { + from: 68, + to: 74, }, }, ]; @@ -336,9 +336,9 @@ describe('Parser', () => { 'https://github.com/jilizart/bbob': 'https://github.com/jilizart/bbob', }, content: [], - startTagPos: { - start: 0, - end: 38, + start: { + from: 0, + to: 38, }, }, ]; @@ -363,13 +363,13 @@ describe('Parser', () => { size: '15', }, content: ['Tag1'], - startTagPos: { - start: 0, - end: 18, + start: { + from: 0, + to: 18, }, - endTagPos: { - start: 22, - end: 31, + end: { + from: 22, + to: 31, }, }, { @@ -378,26 +378,26 @@ describe('Parser', () => { size: '16', }, content: ['Tag2'], - startTagPos: { - start: 31, - end: 49, + start: { + from: 31, + to: 49, }, - endTagPos: { - start: 53, - end: 62, + end: { + from: 53, + to: 62, }, }, { tag: 'mytag3', attrs: {}, content: ['Tag3'], - startTagPos: { - start: 62, - end: 70, + start: { + from: 62, + to: 70, }, - endTagPos: { - start: 74, - end: 83, + end: { + from: 74, + to: 83, }, }, ]; @@ -414,13 +414,13 @@ describe('Parser', () => { tag: 'b', attrs: {}, content: ['hello'], - startTagPos: { - start: 0, - end: 17, + start: { + from: 0, + to: 17, }, - endTagPos: { - start: 24, - end: 31, + end: { + from: 24, + to: 31, }, }, ' ', @@ -430,13 +430,13 @@ describe('Parser', () => { disabled: 'disabled', }, content: ['world'], - startTagPos: { - start: 0, - end: 17, + start: { + from: 0, + to: 17, }, - endTagPos: { - start: 24, - end: 31, + end: { + from: 24, + to: 31, }, }, ]); @@ -452,13 +452,13 @@ describe('Parser', () => { 'https://github.com/JiLiZART/bbob/search?q=any&unscoped_q=any': 'https://github.com/JiLiZART/bbob/search?q=any&unscoped_q=any', }, content: ['GET'], - startTagPos: { - start: 0, - end: 66, + start: { + from: 0, + to: 66, }, - endTagPos: { - start: 69, - end: 75, + end: { + from: 69, + to: 75, }, }, ]); @@ -514,13 +514,13 @@ describe('Parser', () => { " ", "italic", ], - endTagPos: { - "end": 147, - "start": 143, + end: { + to: 147, + from: 143, }, - startTagPos: { - "end": 120, - "start": 117, + start: { + "to": 120, + "from": 117, }, "tag": "i", }, @@ -533,26 +533,26 @@ describe('Parser', () => { " ", "again", ], - endTagPos: { - "end": 170, - "start": 166, + end: { + "to": 170, + "from": 166, }, - startTagPos: { - "end": 104, - "start": 101, + start: { + "to": 104, + "from": 101, }, tag: "b", }, "\n", " ", ], - endTagPos: { - "end": 187, - "start": 177, + end: { + "to": 187, + "from": 177, }, - startTagPos: { - "end": 76, - "start": 16, + start: { + "to": 76, + "from": 16, }, tag: "spoiler", }, @@ -590,13 +590,13 @@ describe('Parser', () => { size: 'xs' }, content: [], - startTagPos: { - start: 82, - end: 164, + start: { + from: 82, + to: 164, }, - endTagPos: { - start: 164, - end: 173, + end: { + from: 164, + to: 173, }, }, '\n', @@ -607,13 +607,13 @@ describe('Parser', () => { ' ', 'Go', ], - startTagPos: { - start: 0, - end: 73, + start: { + from: 0, + to: 73, }, - endTagPos: { - start: 196, - end: 202, + end: { + from: 196, + to: 202, }, }, ' ', @@ -630,13 +630,13 @@ describe('Parser', () => { href: 'https://docs.google.com/spreadsheets/d/1W9VPUESF_NkbSa_HtRFrQNl0nYo8vPCxJFy7jD3Tpio/edit#gid=0', }, content: ['Docs'], - startTagPos: { - start: 0, - end: 105, + start: { + from: 0, + to: 105, }, - endTagPos: { - start: 109, - end: 115, + end: { + from: 109, + to: 115, }, }, ]); @@ -654,24 +654,24 @@ sdfasdfasdf [ { tag: 'quote', attrs: {}, content: ['some'], - startTagPos: { - start: 0, - end: 7, + start: { + from: 0, + to: 7, }, - endTagPos: { - start: 11, - end: 19, + end: { + from: 11, + to: 19, }, }, { tag: 'color', attrs: { red: 'red' }, content: ['test'], - startTagPos: { - start: 19, - end: 30, + start: { + from: 19, + to: 30, }, - endTagPos: { - start: 34, - end: 42, + end: { + from: 34, + to: 42, }, }, '\n', @@ -683,20 +683,20 @@ sdfasdfasdf '\n', { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], - startTagPos: { - start: 74, - end: 83, + start: { + from: 74, + to: 83, }, - endTagPos: { - start: 86, - end: 92, + end: { + from: 86, + to: 92, }, } ] ); }); - test('parse with lost closing tag on start', () => { + test('parse with lost closing tag on from', () => { const str = `[quote]xxxsdfasdf[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url]`; expectOutput( @@ -706,43 +706,43 @@ sdfasdfasdf 'xxxsdfasdf', { tag: 'quote', attrs: {}, content: ['some'], - startTagPos: { - start: 17, - end: 24, + start: { + from: 17, + to: 24, }, - endTagPos: { - start: 28, - end: 36, + end: { + from: 28, + to: 36, }, }, { tag: 'color', attrs: { red: 'red' }, content: ['test'], - startTagPos: { - start: 36, - end: 47, + start: { + from: 36, + to: 47, }, - endTagPos: { - start: 51, - end: 59, + end: { + from: 51, + to: 59, }, }, 'sdfasdfasdf', { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], - startTagPos: { - start: 70, - end: 79, + start: { + from: 70, + to: 79, }, - endTagPos: { - start: 82, - end: 88, + end: { + from: 82, + to: 88, }, } ] ); }); - test('parse with lost closing tag on end', () => { + test('parse with lost closing tag on to', () => { const str = `[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url][quote]xxxsdfasdf`; expectOutput( @@ -750,36 +750,36 @@ sdfasdfasdf [ { tag: 'quote', attrs: {}, content: ['some'], - startTagPos: { - start: 0, - end: 7, + start: { + from: 0, + to: 7, }, - endTagPos: { - start: 11, - end: 19, + end: { + from: 11, + to: 19, }, }, { tag: 'color', attrs: { red: 'red' }, content: ['test'], - startTagPos: { - start: 19, - end: 30, + start: { + from: 19, + to: 30, }, - endTagPos: { - start: 34, - end: 42, + end: { + from: 34, + to: 42, }, }, 'sdfasdfasdf', { tag: 'url', attrs: { xxx: 'xxx' }, content: ['xxx'], - startTagPos: { - start: 53, - end: 62, + start: { + from: 53, + to: 62, }, - endTagPos: { - start: 65, - end: 71, + end: { + from: 65, + to: 71, }, }, '[quote]', @@ -808,13 +808,13 @@ sdfasdfasdf " ", "title=\"value1\"" ], - startTagPos: { - start: 0, - end: 49, + start: { + from: 0, + to: 49, }, - endTagPos: { - start: 78, - end: 87, + end: { + from: 78, + to: 87, }, } ]); @@ -838,13 +838,13 @@ sdfasdfasdf " ", "disabled" ], - startTagPos: { - start: 0, - end: 50, + start: { + from: 0, + to: 50, }, - endTagPos: { - start: 71, - end: 80, + end: { + from: 71, + to: 80, }, } ]); @@ -865,13 +865,13 @@ sdfasdfasdf "content": [ "class=\"value4\"title=\"value5\"" ], - startTagPos: { - start: 0, - end: 48, + start: { + from: 0, + to: 48, }, - endTagPos: { - start: 76, - end: 85, + end: { + from: 76, + to: 85, }, } ]); diff --git a/packages/bbob-plugin-helper/src/TagNode.ts b/packages/bbob-plugin-helper/src/TagNode.ts index 8bb6192c..68857f0d 100644 --- a/packages/bbob-plugin-helper/src/TagNode.ts +++ b/packages/bbob-plugin-helper/src/TagNode.ts @@ -57,8 +57,8 @@ export class TagNode implements TagNodeObject { public readonly tag: string | TagValue; public attrs: Record; public content: TagNodeTree; - public startTagPos?: { start: number; end: number; }; - public endTagPos?: { start: number; end: number; }; + public start?: { from: number; to: number; }; + public end?: { from: number; to: number; }; constructor(tag: string | TagValue, attrs: Record, content: TagNodeTree) { this.tag = tag; @@ -78,12 +78,12 @@ export class TagNode implements TagNodeObject { return appendToNode(this, value); } - setStartTagPos(start: number, end: number) { - this.startTagPos = { start, end }; + setStart(from: number, to: number) { + this.start = { from, to }; } - setEndTagPos(start: number, end: number) { - this.endTagPos = { start, end }; + setEnd(from: number, to: number) { + this.end = { from, to }; } get length(): number { @@ -102,11 +102,11 @@ export class TagNode implements TagNodeObject { toTagNode() { const newNode = new TagNode(String(this.tag).toLowerCase(), this.attrs, this.content); - if (this.startTagPos) { - newNode.setStartTagPos(this.startTagPos.start, this.startTagPos.end); + if (this.start) { + newNode.setStart(this.start.from, this.start.to); } - if (this.endTagPos) { - newNode.setEndTagPos(this.endTagPos.start, this.endTagPos.end); + if (this.end) { + newNode.setEnd(this.end.from, this.end.to); } return newNode; } diff --git a/packages/bbob-types/src/common.ts b/packages/bbob-types/src/common.ts index 4ca745c1..e50e0a13 100644 --- a/packages/bbob-types/src/common.ts +++ b/packages/bbob-types/src/common.ts @@ -4,8 +4,8 @@ export interface TagNodeObject { readonly tag: TagValue; attrs?: Record; content?: TagNodeTree; - startTagPos?: { start: number; end: number; }; - endTagPos?: { start: number; end: number; }; + start?: { from: number; to: number; }; + end?: { from: number; to: number; }; } export type NodeContent = TagNodeObject | StringNode | null; diff --git a/packages/bbob-types/src/parser.ts b/packages/bbob-types/src/parser.ts index 3ac7736f..31e67675 100644 --- a/packages/bbob-types/src/parser.ts +++ b/packages/bbob-types/src/parser.ts @@ -10,13 +10,13 @@ export interface TagNode { readonly tag: string attrs?: Record content?: TagNodeTree, - startTagPos?: { - start: number; - end: number; + start?: { + from: number; + to: number; }; - endTagPos?: { - start: number; - end: number; + end?: { + from: number; + to: number; }; } From 3af485abd2aedac0f264933fdd5f0074dea5330e Mon Sep 17 00:00:00 2001 From: Alteras1 <42795314+Alteras1@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:55:15 -0700 Subject: [PATCH 3/3] Update static TagNode.create to ingest setStart() logic improve readability of end pos offset for no attr tags --- packages/bbob-parser/src/lexer.ts | 4 ++- packages/bbob-parser/src/parse.ts | 5 ++-- packages/bbob-plugin-helper/src/TagNode.ts | 26 +++++++++++-------- .../bbob-plugin-helper/test/TagNode.test.ts | 21 +++++++++++++-- packages/bbob-types/src/common.ts | 6 +++-- packages/bbob-types/src/parser.ts | 12 +++------ 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/packages/bbob-parser/src/lexer.ts b/packages/bbob-parser/src/lexer.ts index 10c7d0aa..0ca43d6c 100644 --- a/packages/bbob-parser/src/lexer.ts +++ b/packages/bbob-parser/src/lexer.ts @@ -34,6 +34,7 @@ const TAG_STATE_VALUE = 2; const WHITESPACES = [SPACE, TAB]; const SPECIAL_CHARS = [EQ, SPACE, TAB]; +const END_POS_OFFSET = 2; // length + start position offset const isWhiteSpace = (char: string) => (WHITESPACES.indexOf(char) >= 0); const isEscapeChar = (char: string) => char === BACKSLASH; @@ -218,10 +219,11 @@ export function createLexer(buffer: string, options: LexerOptions = {}): LexerTo if (isNoAttrsInTag || isClosingTag) { const startPos = chars.getPos() - 1; const name = chars.grabWhile((char) => char !== closeTag); + const endPos = startPos + name.length + END_POS_OFFSET; chars.skip(); // skip closeTag - emitToken(TYPE_TAG, name, startPos, startPos + name.length + 2); + emitToken(TYPE_TAG, name, startPos, endPos); checkContextFreeMode(name, isClosingTag); return STATE_WORD; diff --git a/packages/bbob-parser/src/parse.ts b/packages/bbob-parser/src/parse.ts index 8e15baf7..4d5bb14f 100644 --- a/packages/bbob-parser/src/parse.ts +++ b/packages/bbob-parser/src/parse.ts @@ -185,8 +185,7 @@ function parse(input: string, opts: ParseOptions = {}) { function handleTagStart(token: Token) { flushTagNodes(); - const tagNode = TagNode.create(token.getValue(), {}, []); - tagNode.setStart(token.getStart(), token.getEnd()); + const tagNode = TagNode.create(token.getValue(), {}, [], { from: token.getStart(), to: token.getEnd() }); const isNested = isTokenNested(token); tagNodes.push(tagNode); @@ -206,7 +205,7 @@ function parse(input: string, opts: ParseOptions = {}) { function handleTagEnd(token: Token) { const lastTagNode = nestedNodes.last(); if (isTagNode(lastTagNode)) { - lastTagNode.setEnd(token.getStart(), token.getEnd()); + lastTagNode.setEnd({ from: token.getStart(), to: token.getEnd() }); } flushTagNodes(); diff --git a/packages/bbob-plugin-helper/src/TagNode.ts b/packages/bbob-plugin-helper/src/TagNode.ts index 68857f0d..f6323e46 100644 --- a/packages/bbob-plugin-helper/src/TagNode.ts +++ b/packages/bbob-plugin-helper/src/TagNode.ts @@ -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 { @@ -57,8 +57,8 @@ export class TagNode implements TagNodeObject { public readonly tag: string | TagValue; public attrs: Record; public content: TagNodeTree; - public start?: { from: number; to: number; }; - public end?: { from: number; to: number; }; + public start?: TagPosition; + public end?: TagPosition; constructor(tag: string | TagValue, attrs: Record, content: TagNodeTree) { this.tag = tag; @@ -78,12 +78,12 @@ export class TagNode implements TagNodeObject { return appendToNode(this, value); } - setStart(from: number, to: number) { - this.start = { from, to }; + setStart(value: TagPosition) { + this.start = value; } - setEnd(from: number, to: number) { - this.end = { from, to }; + setEnd(value: TagPosition) { + this.end = value; } get length(): number { @@ -103,10 +103,10 @@ export class TagNode implements TagNodeObject { toTagNode() { const newNode = new TagNode(String(this.tag).toLowerCase(), this.attrs, this.content); if (this.start) { - newNode.setStart(this.start.from, this.start.to); + newNode.setStart(this.start); } if (this.end) { - newNode.setEnd(this.end.from, this.end.to); + newNode.setEnd(this.end); } return newNode; } @@ -122,8 +122,12 @@ export class TagNode implements TagNodeObject { return `${tagStart}${content}${this.toTagEnd({ openTag, closeTag })}`; } - static create(tag: string, attrs: Record = {}, content: TagNodeTree = null) { - return new TagNode(tag, attrs, content); + static create(tag: string, attrs: Record = {}, 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) { diff --git a/packages/bbob-plugin-helper/test/TagNode.test.ts b/packages/bbob-plugin-helper/test/TagNode.test.ts index 03833426..80d83471 100644 --- a/packages/bbob-plugin-helper/test/TagNode.test.ts +++ b/packages/bbob-plugin-helper/test/TagNode.test.ts @@ -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']); diff --git a/packages/bbob-types/src/common.ts b/packages/bbob-types/src/common.ts index e50e0a13..55c345c5 100644 --- a/packages/bbob-types/src/common.ts +++ b/packages/bbob-types/src/common.ts @@ -4,8 +4,8 @@ export interface TagNodeObject { readonly tag: TagValue; attrs?: Record; content?: TagNodeTree; - start?: { from: number; to: number; }; - end?: { from: number; to: number; }; + start?: TagPosition; + end?: TagPosition; } export type NodeContent = TagNodeObject | StringNode | null; @@ -13,3 +13,5 @@ export type NodeContent = TagNodeObject | export type PartialNodeContent = Partial> | StringNode | null; export type TagNodeTree = NodeContent | NodeContent[] | null; + +export type TagPosition = { from: number; to: number; }; diff --git a/packages/bbob-types/src/parser.ts b/packages/bbob-types/src/parser.ts index 31e67675..abe1244f 100644 --- a/packages/bbob-types/src/parser.ts +++ b/packages/bbob-types/src/parser.ts @@ -1,4 +1,4 @@ -import { TagNodeTree } from "./common"; +import { TagNodeTree, TagPosition } from "./common"; export interface ParseError { tagName: string; @@ -10,14 +10,8 @@ export interface TagNode { readonly tag: string attrs?: Record content?: TagNodeTree, - start?: { - from: number; - to: number; - }; - end?: { - from: number; - to: number; - }; + start?: TagPosition; + end?: TagPosition; } export interface Token {