diff --git a/graph-selector/src/getIndentSize.ts b/graph-selector/src/getIndentSize.ts new file mode 100644 index 0000000..69f858c --- /dev/null +++ b/graph-selector/src/getIndentSize.ts @@ -0,0 +1,4 @@ +export function getIndentSize(line: string) { + const match = line.match(/^\s*/); + return match ? match[0].length : 0; +} diff --git a/graph-selector/src/parse.ts b/graph-selector/src/parse.ts index 4a293f1..3aea32e 100644 --- a/graph-selector/src/parse.ts +++ b/graph-selector/src/parse.ts @@ -7,6 +7,7 @@ import { matchAndRemovePointers } from "./matchAndRemovePointers"; // @ts-ignore import { strip } from "@tone-row/strip-comments"; import { ParseError } from "./ParseError"; +import { getIndentSize } from "./getIndentSize"; // TODO: these types could probably be improved to match the target types (in ./types.ts) more closely @@ -399,11 +400,6 @@ export function parse(text: string): Graph { }; } -function getIndentSize(line: string) { - const match = line.match(/^\s*/); - return match ? match[0].length : 0; -} - function findParent(indentSize: number, ancestors: Ancestors): Ancestor { let parent: Ancestor = null; let i = indentSize - 1; diff --git a/graph-selector/src/stringify.test.ts b/graph-selector/src/stringify.test.ts index 1fe1158..a191d61 100644 --- a/graph-selector/src/stringify.test.ts +++ b/graph-selector/src/stringify.test.ts @@ -594,4 +594,183 @@ Step 5 #n9 Step 7 #n10 (#n8)`); }); + + it("can also return a compact version", () => { + const result = stringify( + { + edges: [ + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n1", + target: "n2", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n2", + target: "n3", + }, + { + data: { + classes: "", + id: "", + label: "label", + }, + source: "n3", + target: "n4", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n3", + target: "n6", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n3", + target: "n9", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n4", + target: "n10", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n6", + target: "n7", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n7", + target: "n8", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n9", + target: "n10", + }, + { + data: { + classes: "", + id: "", + label: "", + }, + source: "n10", + target: "n8", + }, + ], + nodes: [ + { + data: { + classes: "", + id: "n1", + label: "Start", + }, + }, + { + data: { + classes: "", + id: "n2", + label: "Step 1", + }, + }, + { + data: { + classes: "", + id: "n3", + label: "Step 2", + }, + }, + { + data: { + classes: "", + id: "n4", + label: "Step 3", + }, + }, + { + data: { + classes: "", + id: "n6", + label: "Step 4", + }, + }, + { + data: { + classes: "", + id: "n7", + label: "Step 6", + }, + }, + { + data: { + classes: "", + id: "n8", + label: "Finish", + }, + }, + { + data: { + classes: "", + id: "n9", + label: "Step 5", + }, + }, + { + data: { + classes: "", + id: "n10", + label: "Step 7", + }, + }, + ], + }, + { + compact: true, + }, + ); + + expect(result).toBe(`Start #n1 + Step 1 #n2 + Step 2 #n3 + label: Step 3 #n4 + Step 7 #n10 + (#n8) + Step 4 #n6 + Step 6 #n7 + Finish #n8 + Step 5 #n9 + (#n10)`); + }); }); diff --git a/graph-selector/src/stringify.ts b/graph-selector/src/stringify.ts index 2f25346..b010593 100644 --- a/graph-selector/src/stringify.ts +++ b/graph-selector/src/stringify.ts @@ -1,3 +1,4 @@ +import { getIndentSize } from "./getIndentSize"; import { Graph } from "./types"; type StringifyOptions = { @@ -14,7 +15,7 @@ const defaultOptions: StringifyOptions = { /** * Convets a graph to graph-selector DSL */ -export function stringify(graph: Graph, _options: StringifyOptions = defaultOptions) { +export function stringify(graph: Graph, options: StringifyOptions = defaultOptions) { const lines: string[] = []; // In compact mode, we will store where edges should be added @@ -41,9 +42,35 @@ export function stringify(graph: Graph, _options: StringifyOptions = defaultOpti // Only include data if there is any const data = stringifyData(node); + // build the line const features = [idStr, classesStr, data].filter(Boolean).join(""); const line = [labelStr, features].filter(Boolean).join(" "); - lines.push(line); + + // Store how indented the parent is for use in compact mode + let parentIndent = 0; + let insertEdgeAt: number | undefined = undefined; + + if (options.compact) { + // check if there is an edge to this id alread in the list of lines + const targetStr = idStr ? `(${idStr})` : `(${labelStr})`; + const existingLineIndex = lines.findIndex((line) => line.includes(targetStr)); + + // If this node has not been referenced yet, add it to the list of lines + if (existingLineIndex === -1) { + lines.push(line); + } else { + // replace the targetStr with the line + lines[existingLineIndex] = lines[existingLineIndex]!.replace(targetStr, line); + + // set the insertEdgeAt to the index of the next line + insertEdgeAt = existingLineIndex + 1; + + // set the parentIndent to the indent of the existing line + parentIndent = getIndentSize(lines[existingLineIndex]!); + } + } else { + lines.push(line); + } // get all edges whose source is this node const edges = graph.edges.filter((edge) => edge.source === node.id); @@ -84,8 +111,16 @@ export function stringify(graph: Graph, _options: StringifyOptions = defaultOpti // wrap link in () const wrappedLink = `(${link})`; - const line = [" ", label, wrappedLink].filter(Boolean).join(""); - lines.push(line); + const parentIndentStr = " ".repeat(parentIndent); + + const line = [parentIndentStr, " ", label, wrappedLink].filter(Boolean).join(""); + + if (options.compact && insertEdgeAt !== undefined) { + lines.splice(insertEdgeAt, 0, line); + insertEdgeAt++; + } else { + lines.push(line); + } } }