diff --git a/experiments/text-nodes.mjs b/experiments/text-nodes.mjs index d05c907..36ce7ac 100644 --- a/experiments/text-nodes.mjs +++ b/experiments/text-nodes.mjs @@ -1,47 +1,9 @@ import * as CF from '@efforting.tech/data/field-configuration-factories'; -import { readFileSync } from 'node:fs'; import { inspect } from 'node:util'; -import { Indention_Mode, Text_Settings } from '@efforting.tech/text/basic-tree'; - -// TODO: Move into string helper module -function string_has_contents(str) { - return /\S/.test(str); -} - -function *indented_line_iterator(settings, text) { - - let line_no = settings.first_line; - let index = 0; - const { indention_tabulator_width } = settings; - - switch (settings.indention_mode) { - case Indention_Mode.symbols.TABULATORS: { - for (const line of text.matchAll(/^(\t*)(.*)$/gm)) { - const [raw, tabs, remaining] = line; - yield { raw, indent: tabs.length, line: remaining, line_no: line_no++, index: index++}; - } - break; - } - case Indention_Mode.symbols.SPACES: { - for (const line of text.matchAll(/^([ ]*)(.*)$/gm)) { - const [raw, spaces, remaining] = line; - - if ((spaces.length % indention_tabulator_width) !== 0) { - throw new Error('Unaligned indention'); //TODO - proper error - } - yield { raw, indent: Math.floor(spaces.length / indention_tabulator_width), line: remaining, line_no: line_no++, index: index++}; - } - break; - } - default: - throw new Error('Unsupported indention mode'); //TODO - proper error - - } - - -} +import { Indention_Mode, Text_Settings, string_has_contents, indented_line_iterator } from '@efforting.tech/data/string-utilities'; +import { Text_Node } from '@efforting.tech/text/basic-tree'; const ts = Text_Settings.load({ @@ -68,61 +30,9 @@ branch3 `; -class Text_Node { - constructor(text_settings, line=undefined, indent=0, line_no=undefined, index=undefined, raw=undefined, parent=undefined) { - Object.assign(this, { text_settings, line, indent, line_no, index, raw, parent, children: [] }); - } -} +const root = Text_Node.from_string(ts, example_string); -const root = new Text_Node(ts); - -// NOTE: This first Text_Node is not added to the tree, it serves as an initial cursor only. -let current = new Text_Node(root.text_settings, undefined, 0, undefined, undefined, undefined, root); - - -for (const line_info of indented_line_iterator(ts, example_string)) { - - // TODO: Implement other variants than inherit-from-previous - const indent = string_has_contents(line_info.line) ? line_info.indent : current.indent; - - const delta_indent = indent - current.indent; - - if (delta_indent == 0) { - const pending = new Text_Node(current.text_settings, undefined, current.indent, undefined, undefined, undefined, current.parent); // Partial insertion - same level - if (current.parent) { - current.parent.children.push(pending); - } - current = pending; - } else if (delta_indent > 0) { - for (let i=0; idelta_indent; i--) { - current = current.parent; - } - - const pending = new Text_Node(current.text_settings, undefined, current.indent, undefined, undefined, undefined, current.parent); // Partial insertion - same level - if (current.parent) { - current.parent.children.push(pending); - } - current = pending; - } - - // Fill in partial insertion - Object.assign(current, { - line: line_info.line, - line_no: line_info.line_no, - index: line_info.index, - raw: line_info.raw, - }); - - -} - function debug_dump(node, level=0) { console.log(`${" ".repeat(level)}[${node.line_no ?? '-'}] ${inspect(node.line)}`); @@ -132,6 +42,8 @@ function debug_dump(node, level=0) { } debug_dump(root); + + /* [-] undefined diff --git a/package-manifest.yaml b/package-manifest.yaml index e8dbd99..d20d39c 100644 --- a/package-manifest.yaml +++ b/package-manifest.yaml @@ -1,6 +1,6 @@ scope: '@efforting.tech' registry: 'https://npm.efforting.tech/' -version: 0.2.8 +version: 0.2.9 author: name: 'Mikael Lövqvist' diff --git a/source/data/string-utilities.mjs b/source/data/string-utilities.mjs new file mode 100644 index 0000000..cd0696f --- /dev/null +++ b/source/data/string-utilities.mjs @@ -0,0 +1,51 @@ +import * as CF from '@efforting.tech/data/field-configuration-factories'; + + +export const Indention_Mode = new CF.symbol_set({ + AUTO: 'Automatic detection of indention mode', + SPACES: 'Indention is based on spaces', + TABULATORS: 'Indention is based on tabulators', +}, 'Indention mode'); + + +// BUG: Current implementation of CF.symbol_set doesn't support default value +export const Text_Settings = new CF.Schema({ + indention_mode: Indention_Mode, + indention_tabulator_width: CF.cardinal_value(4, 'Width of a tabulator in spaces'), + first_line: CF.natural_value(1, 'First line number'), +}, 'Text settings'); + + +export function string_has_contents(str) { + return /\S/.test(str); +} + + +export function *indented_line_iterator(settings, text) { + let line_no = settings.first_line; + let index = 0; + const { indention_tabulator_width } = settings; + + switch (settings.indention_mode) { + case Indention_Mode.symbols.TABULATORS: { + for (const line of text.matchAll(/^(\t*)(.*)$/gm)) { + const [raw, tabs, remaining] = line; + yield { raw, indent: tabs.length, line: remaining, line_no: line_no++, index: index++}; + } + break; + } + case Indention_Mode.symbols.SPACES: { + for (const line of text.matchAll(/^([ ]*)(.*)$/gm)) { + const [raw, spaces, remaining] = line; + + if ((spaces.length % indention_tabulator_width) !== 0) { + throw new Error('Unaligned indention'); //TODO - proper error + } + yield { raw, indent: Math.floor(spaces.length / indention_tabulator_width), line: remaining, line_no: line_no++, index: index++}; + } + break; + } + default: + throw new Error('Unsupported indention mode'); //TODO - proper error + } +} diff --git a/source/text/basic-tree.mjs b/source/text/basic-tree.mjs index 9fe932e..c9d6c81 100644 --- a/source/text/basic-tree.mjs +++ b/source/text/basic-tree.mjs @@ -1,16 +1,64 @@ -import * as CF from '@efforting.tech/data/field-configuration-factories'; +import { string_has_contents, indented_line_iterator } from '@efforting.tech/data/string-utilities'; + +export class Text_Node { + constructor(text_settings, line=undefined, indent=0, line_no=undefined, index=undefined, raw=undefined, parent=undefined) { + Object.assign(this, { text_settings, line, indent, line_no, index, raw, parent, children: [] }); + } + + static from_string(text_settings, str) { + + const root = new this(text_settings); + + // NOTE: This first Text_Node is not added to the tree, it serves as an initial cursor only. + let current = new this(root.text_settings, undefined, 0, undefined, undefined, undefined, root); + + + for (const line_info of indented_line_iterator(text_settings, str)) { + + // TODO: Implement other variants than inherit-from-previous + const indent = string_has_contents(line_info.line) ? line_info.indent : current.indent; + + const delta_indent = indent - current.indent; + + if (delta_indent == 0) { + const pending = new Text_Node(current.text_settings, undefined, current.indent, undefined, undefined, undefined, current.parent); // Partial insertion - same level + if (current.parent) { + current.parent.children.push(pending); + } + current = pending; + } else if (delta_indent > 0) { + for (let i=0; idelta_indent; i--) { + current = current.parent; + } + + const pending = new Text_Node(current.text_settings, undefined, current.indent, undefined, undefined, undefined, current.parent); // Partial insertion - same level + if (current.parent) { + current.parent.children.push(pending); + } + current = pending; + } + + // Fill in partial insertion + Object.assign(current, { + line: line_info.line, + line_no: line_info.line_no, + index: line_info.index, + raw: line_info.raw, + }); + + + } + return root; + + } +} -export const Indention_Mode = new CF.symbol_set({ - AUTO: 'Automatic detection of indention mode', - SPACES: 'Indention is based on spaces', - TABULATORS: 'Indention is based on tabulators', -}, 'Indention mode'); -// BUG: Current implementation of CF.symbol_set doesn't support default value -export const Text_Settings = new CF.Schema({ - indention_mode: Indention_Mode, - indention_tabulator_width: CF.cardinal_value(4, 'Width of a tabulator in spaces'), - first_line: CF.natural_value(1, 'First line number'), -}, 'Text settings');