// StrikethruDistancesToNotes.lobster
// Implements:
// (01) iterate current selection rectangle
// (02) recursively assess stylebits in selected cells' subtrees
// (03) collect (x,y) for strikethru cells (bit 16)
// (04) compute pairwise parent/child tree distance
// (05) append distance lines into each cell's NOTE (not cell text)
// (06) end
import std
let STRIKETHRU = 16
// Collected marked nodes:
let node_paths = [] // each is an array of child indices from selection-parent root
let node_texts = [] // ts.get_text() captured for each marked node
let node_xs = [] // x coordinate within its parent grid
let node_ys = [] // y coordinate within its parent grid
// Reusable traversal stacks:
var path_stack = []
var col_stack = [] // columns count of parent grid at each depth
var depth = 0
def ensure_stack_capacity() -> void:
if path_stack.length == depth:
path_stack.push(0)
col_stack.push(0)
def copy_prefix(p, n):
let out = []
for(n) i:
out.push(p[i])
return out
def record_if_strikethru() -> void:
// depth==0 means "no path" (should not happen once we're inside a selected cell)
if depth == 0: return
if ts.get_style_bits() & STRIKETHRU:
node_paths.push(copy_prefix(path_stack, depth))
node_texts.push(ts.get_text())
// Coordinate of this node within its parent grid:
let idx = path_stack[depth - 1]
let cols = col_stack[depth - 1]
node_xs.push(idx % cols)
node_ys.push(idx / cols)
def walk_subtree() -> void:
record_if_strikethru()
if ts.num_children():
let g = ts.num_columns_rows() // g[0]=cols, g[1]=rows
for(ts.num_children()) i:
ensure_stack_capacity()
path_stack[depth] = i
col_stack[depth] = g[0]
depth++
ts.goto_child(i)
walk_subtree()
ts.goto_parent()
depth--
def lcp(a, b):
let m = if a.length < b.length: a.length else: b.length
var k = 0
for(m) i:
if a[i] == b[i]:
k++
else:
break
return k
def dist(a, b):
let k = lcp(a, b)
return (a.length - k) + (b.length - k)
def goto_selection_parent() -> void:
ts.goto_selection()
ts.goto_parent()
def goto_node(p) -> void:
goto_selection_parent()
for(p.length) i:
ts.goto_child(p[i])
def build_lines(i):
let parts = []
for(node_paths.length) j:
if i != j:
let d = dist(node_paths[i], node_paths[j])
// Required format: (<number> , <text of related cell compared to> )
parts.push("({d} , {node_texts[j]})\n")
return concat_string(parts, "")
def write_lines_into_note(lines) -> void:
let old = ts.get_note()
// Assumption: "has related note of its own" ≈ old note non-empty.
// If non-empty: append at end. If empty: set note to new lines (effectively at beginning).
if old.length:
ts.set_note(old + "\n" + lines)
else:
ts.set_note(lines)
// -------------------- main --------------------
if not ts.has_selection():
ts.set_status_message("No selection rectangle; please select a region first.")
else:
ts.goto_selection()
let size, start = ts.selection() // selection() -> (size:int2, start:int2)
ts.goto_parent()
let g = ts.num_columns_rows() // g[0]=cols, g[1]=rows
// Enumerate selected cells in this grid (same pattern as Statistics.lobster).
for(ts.num_children()) j:
let x = j % g[0]
let y = j / g[0]
if x >= start[0] and x < start[0] + size[0] and y >= start[1] and y < start[1] + size[1]:
// Start path at this selected cell index j.
ensure_stack_capacity()
path_stack[0] = j
col_stack[0] = g[0]
depth = 1
ts.goto_child(j)
walk_subtree()
ts.goto_parent()
depth = 0
// Write per-node distance info into notes.
for(node_paths.length) i:
let lines = build_lines(i)
if lines.length:
goto_node(node_paths[i])
write_lines_into_note(lines)
ts.set_status_message("Updated notes for {node_paths.length} strikethru cell(s).")