import { string_has_contents, indented_line_iterator } from '@efforting.tech/data/string-utilities'; import * as CF from '@efforting.tech/data/field-configuration-factories'; import { Text_Settings } from '@efforting.tech/data/string-utilities'; export const Text_Tree_Settings = new CF.Schema({ //BUG - there is currently no way (I think) to put defaults into a sub schema - this should be fixed text: Text_Settings, trim_head: CF.boolean(false, 'Trim the empty lines from the head of a node'), trim_tail: CF.boolean(false, 'Trim the empty lines from the tail of a node'), trim_lines: CF.boolean(false, 'Trim lines'), }, 'Text tree settings'); export class Text_Tree_Node { constructor(text_tree_settings, line=undefined, indent=0, line_no=undefined, index=undefined, raw=undefined, parent=undefined) { Object.assign(this, { text_tree_settings, line, indent, line_no, index, raw, parent, children: [] }); } get has_line() { return string_has_contents(this.line); } static from_string(text_tree_settings, str) { const root = new this(text_tree_settings); const { trim_head, trim_tail, trim_lines } = text_tree_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_tree_settings, undefined, 0, undefined, undefined, undefined, root); for (const line_info of indented_line_iterator(text_tree_settings.text, 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 this(current.text_tree_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 this(current.text_tree_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: trim_lines ? line_info.line.trim() : line_info.line, line_no: line_info.line_no, index: line_info.index, raw: line_info.raw, }); } if (trim_head || trim_tail) { //TODO: Implement throw new Error('Trimming is not implemented'); //TODO: Proper non implemented error } return root; } }