import { ITree, IBaseType, getStringWidth as sw } from '../../../'; import { ILogOptions, ICharMatrix, Cable } from '../type'; import { DEFAULT_TREENODE_SPACING } from '../const'; import { parse } from './'; interface ITreeInfo { val: ICharMatrix; valLength: number; width: number; offset: number[][]; margin: number[]; childs: ITreeInfo[]; } function getTreeInfo(tree: ITree): ITreeInfo { const vl = parse(tree.val).w | 1; const h = { val: parse(tree.val), valLength: vl, margin: new Array(tree.childs.length).fill(0), childs: tree.childs.map(v => getTreeInfo(v)), } as ITreeInfo; const { margin: m, childs: c } = h; if (c.length === 0) { h.width = vl; h.offset = [[0, vl]]; return h; } const o = c[0].offset.map(v => v.slice()); let w = c[0].width; // 整体宽度 for (let i = 1; i < c.length; ++i) { const { width: wi, offset: oi } = c[i]; let maxMargin = -w; for (let j = 0, jl = Math.min(o.length, oi.length); j < jl; ++j) { maxMargin = Math.max(maxMargin, DEFAULT_TREENODE_SPACING - w - oi[j][0] + o[j][1]); } for (let j = 0, jl = Math.max(o.length, oi.length); j < jl; ++j) { if (!o[j]) { o[j] = [w + maxMargin + oi[j][0], 0]; } if (oi[j]) { o[j][1] = w + maxMargin + oi[j][1]; } } m[i] = maxMargin; w += maxMargin + wi; } const ca = c[0]; const cb = c[c.length - 1]; let myOffset = ((ca.offset[0][0] + (ca.valLength >> 1) + (w - (cb.width - cb.offset[0][1] + (cb.valLength >> 1) + 1))) >> 1) - (vl >> 1); if (myOffset < 0) { m[0] = -myOffset; w -= myOffset; o.forEach(v => { v[0] -= myOffset; v[1] -= myOffset; }); myOffset = 0; } o.unshift([myOffset, myOffset + vl]); h.width = Math.max(w, ...o.map(v => v[1])); h.offset = o; return h; } export function parseTree(tree: ITree, options: ILogOptions): ICharMatrix { const root = getTreeInfo(tree); const strs: Array> = new Array(root.offset.length * 2 - 1).fill(0).map(() => []); const lens: number[] = new Array(strs.length).fill(0); let task = [{ h: root, p: 0 }]; let level = 0; while (task.length) { const newTask: typeof task = []; task.forEach(({ h, p }) => { const { val: v, valLength: vl, offset: o, margin: m, width: w, childs: c } = h; let i = level * 2; // 处理节点 strs[i].push(' '.repeat(p + o[0][0] - lens[i])); strs[i].push(v); lens[i] = p + o[0][0] + v.w; i++; // 处理连线 if (c.length) { const cl = c.length; const u = o[0][0] + (vl >> 1); let ls: any = ''; let newP = m[0]; c.forEach((ci, i) => { newTask.push({ h: ci, p: p + newP }); ls += (i === 0 ? ' ' : Cable.H).repeat(newP + ci.offset[0][0] + (ci.valLength >> 1) - ls.length); ls += cl === 1 ? Cable.V : i === 0 ? Cable.RB : i === cl - 1 ? Cable.LB : Cable.MB; newP += ci.width + (m[i + 1] || 0); }); strs[i].push(' '.repeat(p + m[0] + c[0].offset[0][0] + (c[0].valLength >> 1) - lens[i])); ls = ls.split(''); ls[u] = ls[u] === Cable.V ? Cable.V : ls[u] === Cable.H ? Cable.MT : Cable.MM; ls = ls.join('').trim(); strs[i].push({ v: [ls], w: sw(ls), h: 1, c: 'magentaBright' }); lens[i] = p + m[0] + c[0].offset[0][0] + (c[0].valLength >> 1) + sw(ls); } }); task = newTask; level++; } const logStrs: string[] = []; for (let i = 0, l = strs.length; i < l; ++i) { const s = strs[i].map(v => (typeof v === 'string' ? v : v.v[0])).join(''); logStrs[i] = s + ' '.repeat(root.width - sw(s)); } return { v: logStrs, w: sw(logStrs[0]), h: logStrs.length }; }