灵符lingfu
L

ling-miao

↓ 0 downloads

miao: a Ling-native version control system — temples, souls, rites and talismans instead of repos, branches, commits and tags; adaptive whole/delta/chunk storage; SSH temple fairs for sharing; a LingStationX-themed GUI; ROYGBIV everywhere

Windows · x86-64macOS · Intel x86-64Linux · x86-64LingOS · Pi 5 ARM64
Keywords vcs, version-control, git, miao, ling
lingfu install ling-miao
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (268)

ling/diffy.ling
# ━━━ diffy.ling — yinyang: line diffs, delta scripts, chunking, merge ━━━
# yin (阴) is what darkens away: deletions, red.
# yang (阳) is what comes to light: additions, green.
# fns prefixed dy_.

# ── core line diff ────────────────────────────────────────────────────
# returns a flat strided ops list: [kind, count, kind, count, ...]
# kinds: "=" advance both, "-" advance a only (yin), "+" advance b only (yang)
fn dy_ops(la, lb) {
    bind na = len(la)
    bind nb = len(lb)
    # common prefix
    bind p = 0.0
    bind lim = min(na, nb)
    bind go = true
    while go {
        if p >= lim {
            bind go = false
        } else if list_get(la, p) == list_get(lb, p) {
            bind p = p + 1.0
        } else {
            bind go = false
        }
    }
    # common suffix (not overlapping the prefix)
    bind s = 0.0
    bind go = true
    while go {
        if s >= lim - p {
            bind go = false
        } else if list_get(la, na - 1.0 - s) == list_get(lb, nb - 1.0 - s) {
            bind s = s + 1.0
        } else {
            bind go = false
        }
    }
    bind ma = na - p - s
    bind mb = nb - p - s
    bind ops = list_new()
    if p > 0.0 {
        bind ops = list_push(ops, "=")
        bind ops = list_push(ops, p)
    }
    if ma > 0.0 || mb > 0.0 {
        if ma * mb > 160000.0 || ma < 0.5 || mb < 0.5 {
            # too large for LCS (or pure insert/delete): one replace block
            if ma > 0.0 {
                bind ops = list_push(ops, "-")
                bind ops = list_push(ops, ma)
            }
            if mb > 0.0 {
                bind ops = list_push(ops, "+")
                bind ops = list_push(ops, mb)
            }
        } else {
            bind mid = dy_lcs_ops(la, lb, p, ma, mb)
            bind k = 0.0
            while k < len(mid) {
                bind ops = list_push(ops, list_get(mid, k))
                bind k = k + 1.0
            }
        }
    }
    if s > 0.0 {
        bind ops = list_push(ops, "=")
        bind ops = list_push(ops, s)
    }
    return ops
}

# LCS over the middle window la[p..p+ma) x lb[p..p+mb); returns strided ops
fn dy_lcs_ops(la, lb, p, ma, mb) {
    # build DP table rows (list of lists), row size mb+1
    bind rows = list_new()
    bind row0 = list_new()
    bind j = 0.0
    while j <= mb {
        bind row0 = list_push(row0, 0.0)
        bind j = j + 1.0
    }
    bind rows = list_push(rows, row0)
    bind i = 1.0
    while i <= ma {
        bind prev = list_get(rows, i - 1.0)
        bind row = list_new()
        bind row = list_push(row, 0.0)
        bind ai = list_get(la, p + i - 1.0)
        bind j = 1.0
        while j <= mb {
            if ai == list_get(lb, p + j - 1.0) {
                bind row = list_push(row, list_get(prev, j - 1.0) + 1.0)
            } else {
                bind row = list_push(row, max(list_get(prev, j), list_get(row, j - 1.0)))
            }
            bind j = j + 1.0
        }
        bind rows = list_push(rows, row)
        bind i = i + 1.0
    }
    # backtrack -> reversed per-step kinds
    bind steps = list_new()
    bind i = ma
    bind j = mb
    bind go = true
    while go {
        if i < 0.5 && j < 0.5 {
            bind go = false
        } else if i < 0.5 {
            bind steps = list_push(steps, "+")
            bind j = j - 1.0
        } else if j < 0.5 {
            bind steps = list_push(steps, "-")
            bind i = i - 1.0
        } else if list_get(la, p + i - 1.0) == list_get(lb, p + j - 1.0) {
            bind steps = list_push(steps, "=")
            bind i = i - 1.0
            bind j = j - 1.0
        } else {
            bind up = list_get(list_get(rows, i - 1.0), j)
            bind lf = list_get(list_get(rows, i), j - 1.0)
            if up >= lf {
                bind steps = list_push(steps, "-")
                bind i = i - 1.0
            } else {
                bind steps = list_push(steps, "+")
                bind j = j - 1.0
            }
        }
    }
    # walk steps in reverse, coalescing runs; prefer emitting "-" before "+"
    bind ops = list_new()
    bind k = len(steps) - 1.0
    bind cur = ""
    bind count = 0.0
    while k >= 0.0 {
        bind kind = list_get(steps, k)
        if kind == cur {
            bind count = count + 1.0
        } else {
            if count > 0.0 {
                bind ops = list_push(ops, cur)
                bind ops = list_push(ops, count)
            }
            bind cur = kind
            bind count = 1.0
        }
        bind k = k - 1.0
    }
    if count > 0.0 {
        bind ops = list_push(ops, cur)
        bind ops = list_push(ops, count)
    }
    return ops
}

# ── delta scripts (storage format for text spirits) ───────────────────
# script lines:  c <start> <count>   copy from base
#                i <count>           insert the next <count> literal lines
fn dy_delta_make(base_text, new_text) {
    bind la = split(base_text, "\n")
    bind lb = split(new_text, "\n")
    bind ops = dy_ops(la, lb)
    bind out = ""
    bind apos = 0.0
    bind bpos = 0.0
    bind k = 0.0
    while k < len(ops) {
        bind kind = list_get(ops, k)
        bind n = list_get(ops, k + 1.0)
        if kind == "=" {
            bind out = out + "c " + apos + " " + n + "\n"
            bind apos = apos + n
            bind bpos = bpos + n
        } else if kind == "-" {
            bind apos = apos + n
        } else {
            bind out = out + "i " + n + "\n"
            bind q = 0.0
            while q < n {
                bind out = out + list_get(lb, bpos + q) + "\n"
                bind q = q + 1.0
            }
            bind bpos = bpos + n
        }
        bind k = k + 2.0
    }
    return out
}

fn dy_delta_apply(base_text, script) {
    bind la = split(base_text, "\n")
    bind sl = split(script, "\n")
    bind out = list_new()
    bind i = 0.0
    while i < len(sl) {
        bind ln = list_get(sl, i)
        if str_starts_with(ln, "c ") {
            bind parts = split(ln, " ")
            bind start = to_number(list_get(parts, 1.0))
            bind n = to_number(list_get(parts, 2.0))
            bind q = 0.0
            while q < n {
                bind out = list_push(out, list_get(la, start + q))
                bind q = q + 1.0
            }
            bind i = i + 1.0
        } else if str_starts_with(ln, "i ") {
            bind n = to_number(str_slice(ln, 2.0, 99.0))
            bind q = 0.0
            while q < n {
                bind out = list_push(out, list_get(sl, i + 1.0 + q))
                bind q = q + 1.0
            }
            bind i = i + 1.0 + n
        } else {
            bind i = i + 1.0
        }
    }
    return list_join(out, "\n")
}

# ── fenshen 分身: content-defined chunking over a hex string ──────────
# boundaries at the anchor byte pair 0x2e71, min 64 KiB, max 256 KiB per
# chunk. shift-resistant: an insert only re-stores the chunks it touches.
fn dy_chunks(hx) {
    bind minh = 131072.0
    bind maxh = 524288.0
    bind n = len(hx)
    bind out = list_new()
    bind pos = 0.0
    while pos < n {
        if n - pos <= maxh {
            bind out = list_push(out, str_slice(hx, pos, n - pos))
            bind pos = n
        } else {
            bind win = str_slice(hx, pos + minh, maxh - minh)
            # find an even-aligned anchor
            bind woff = 0.0
            bind f = 0.0 - 1.0
            bind go = true
            while go {
                bind rest = str_slice(win, woff, len(win) - woff)
                bind g = str_find(rest, "2e71")
                if g < 0.0 {
                    bind go = false
                } else if floor((woff + g) / 2.0) * 2.0 == woff + g {
                    bind f = woff + g
                    bind go = false
                } else {
                    bind woff = woff + g + 1.0
                }
            }
            bind cut = pos + maxh
            if f >= 0.0 { bind cut = pos + minh + f + 4.0 }
            bind out = list_push(out, str_slice(hx, pos, cut - pos))
            bind pos = cut
        }
    }
    return out
}

# ── edits extraction (for merge): ops -> [starts, ends, repl_lists] ───
fn dy_edits(base_lines, new_lines) {
    bind ops = dy_ops(base_lines, new_lines)
    bind starts = list_new()
    bind ends = list_new()
    bind repls = list_new()
    bind apos = 0.0
    bind bpos = 0.0
    bind k = 0.0
    bind open = false
    bind es = 0.0
    bind repl = list_new()
    while k < len(ops) {
        bind kind = list_get(ops, k)
        bind n = list_get(ops, k + 1.0)
        if kind == "=" {
            if open {
                bind starts = list_push(starts, es)
                bind ends = list_push(ends, apos)
                bind repls = list_push(repls, repl)
                bind open = false
            }
            bind apos = apos + n
            bind bpos = bpos + n
        } else {
            if open == false {
                bind open = true
                bind es = apos
                bind repl = list_new()
            }
            if kind == "-" {
                bind apos = apos + n
            } else {
                bind q = 0.0
                while q < n {
                    bind repl = list_push(repl, list_get(new_lines, bpos + q))
                    bind q = q + 1.0
                }
                bind bpos = bpos + n
            }
        }
        bind k = k + 2.0
    }
    if open {
        bind starts = list_push(starts, es)
        bind ends = list_push(ends, apos)
        bind repls = list_push(repls, repl)
    }
    return [starts, ends, repls]
}

# ── harmonize: three-way text merge. returns [merged_text, conflicts] ─
fn dy_merge3(base, ours, theirs, label_o, label_t) {
    if ours == theirs { return [ours, 0.0] }
    if ours == base { return [theirs, 0.0] }
    if theirs == base { return [ours, 0.0] }
    bind lbase = split(base, "\n")
    bind eo = dy_edits(lbase, split(ours, "\n"))
    bind et = dy_edits(lbase, split(theirs, "\n"))
    bind os = list_get(eo, 0.0)
    bind oe = list_get(eo, 1.0)
    bind orp = list_get(eo, 2.0)
    bind ts = list_get(et, 0.0)
    bind te = list_get(et, 1.0)
    bind trp = list_get(et, 2.0)
    bind out = list_new()
    bind conflicts = 0.0
    bind cur = 0.0
    bind i = 0.0
    bind j = 0.0
    bind go = true
    while go {
        bind has_o = i < len(os)
        bind has_t = j < len(ts)
        if has_o == false && has_t == false {
            bind go = false
        } else {
            # which edit comes first?
            bind so = 99999999.0
            bind st2 = 99999999.0
            if has_o { bind so = list_get(os, i) }
            if has_t { bind st2 = list_get(ts, j) }
            # overlap test: edits [so,eo) and [st,et) touch?
            bind overlap = false
            if has_o {
                if has_t {
                    bind a1 = list_get(os, i)
                    bind a2 = list_get(oe, i)
                    bind b1 = list_get(ts, j)
                    bind b2 = list_get(te, j)
                    if a1 < b2 && b1 < a2 { bind overlap = true }
                    # two insertions at the same point: order is ambiguous
                    if a1 == b1 && a1 == a2 && b1 == b2 { bind overlap = true }
                }
            }
            if overlap {
                # expand the conflict region across chained overlaps
                bind us = min(list_get(os, i), list_get(ts, j))
                bind ue = max(list_get(oe, i), list_get(te, j))
                bind oi0 = i
                bind tj0 = j
                bind grow = true
                while grow {
                    bind grow = false
                    if i + 1.0 < len(os) {
                        if list_get(os, i + 1.0) <= ue {
                            bind i = i + 1.0
                            bind ue = max(ue, list_get(oe, i))
                            bind grow = true
                        }
                    }
                    if j + 1.0 < len(ts) {
                        if list_get(ts, j + 1.0) <= ue {
                            bind j = j + 1.0
                            bind ue = max(ue, list_get(te, j))
                            bind grow = true
                        }
                    }
                }
                # copy base up to the union start
                bind q = cur
                while q < us {
                    bind out = list_push(out, list_get(lbase, q))
                    bind q = q + 1.0
                }
                # ours version of [us,ue): apply ours edits oi0..i inside the region
                bind out = list_push(out, "<<<<<<< " + label_o)
                bind out = dy_apply_region(out, lbase, us, ue, os, oe, orp, oi0, i)
                bind out = list_push(out, "=======")
                bind out = dy_apply_region(out, lbase, us, ue, ts, te, trp, tj0, j)
                bind out = list_push(out, ">>>>>>> " + label_t)
                bind conflicts = conflicts + 1.0
                bind cur = ue
                bind i = i + 1.0
                bind j = j + 1.0
            } else if so <= st2 {
                # ours edit applies cleanly
                bind q = cur
                while q < so {
                    bind out = list_push(out, list_get(lbase, q))
                    bind q = q + 1.0
                }
                bind r = list_get(orp, i)
                bind q = 0.0
                while q < len(r) {
                    bind out = list_push(out, list_get(r, q))
                    bind q = q + 1.0
                }
                bind cur = list_get(oe, i)
                bind i = i + 1.0
            } else {
                bind q = cur
                while q < st2 {
                    bind out = list_push(out, list_get(lbase, q))
                    bind q = q + 1.0
                }
                bind r = list_get(trp, j)
                bind q = 0.0
                while q < len(r) {
                    bind out = list_push(out, list_get(r, q))
                    bind q = q + 1.0
                }
                bind cur = list_get(te, j)
                bind j = j + 1.0
            }
        }
    }
    bind q = cur
    while q < len(lbase) {
        bind out = list_push(out, list_get(lbase, q))
        bind q = q + 1.0
    }
    return [list_join(out, "\n"), conflicts]
}

# apply one side's edits (ei0..ei1) to base region [us,ue), push onto out
fn dy_apply_region(out, lbase, us, ue, ss, ee, rr, ei0, ei1) {
    bind pos = us
    bind e = ei0
    while e <= ei1 {
        bind s = list_get(ss, e)
        bind q = pos
        while q < s {
            bind out = list_push(out, list_get(lbase, q))
            bind q = q + 1.0
        }
        bind r = list_get(rr, e)
        bind q = 0.0
        while q < len(r) {
            bind out = list_push(out, list_get(r, q))
            bind q = q + 1.0
        }
        bind pos = list_get(ee, e)
        bind e = e + 1.0
    }
    bind q = pos
    while q < ue {
        bind out = list_push(out, list_get(lbase, q))
        bind q = q + 1.0
    }
    return out
}

# ── colored unified diff rendering ────────────────────────────────────
fn dy_render(a_text, b_text, header) {
    bind la = split(a_text, "\n")
    bind lb = split(b_text, "\n")
    bind ops = dy_ops(la, lb)
    bind out = cx_tal("☯ " + header) + "\n"
    bind apos = 0.0
    bind bpos = 0.0
    bind k = 0.0
    bind ctx = 3.0
    while k < len(ops) {
        bind kind = list_get(ops, k)
        bind n = list_get(ops, k + 1.0)
        if kind == "=" {
            bind is_first = k < 1.0
            bind is_last = k + 2.0 >= len(ops)
            # trailing context after the previous hunk
            bind tail = min(ctx, n)
            if is_first { bind tail = 0.0 }
            bind q = 0.0
            while q < tail {
                bind out = out + cx_dim("  " + list_get(la, apos + q)) + "\n"
                bind q = q + 1.0
            }
            # leading context before the next hunk
            bind lead = min(ctx, n - tail)
            if is_last { bind lead = 0.0 }
            if tail + lead < n {
                if is_first == false || lead > 0.0 { bind out = out + cx_meta("  ···") + "\n" }
            }
            bind q = 0.0
            while q < lead {
                bind out = out + cx_dim("  " + list_get(la, apos + n - lead + q)) + "\n"
                bind q = q + 1.0
            }
            bind apos = apos + n
            bind bpos = bpos + n
        } else if kind == "-" {
            bind out = out + cx_meta("@@ 阴 -" + (apos + 1.0) + "," + n + " @@") + "\n"
            bind q = 0.0
            while q < n {
                bind out = out + cx_err("− " + list_get(la, apos + q)) + "\n"
                bind q = q + 1.0
            }
            bind apos = apos + n
        } else {
            bind out = out + cx_meta("@@ 阳 +" + (bpos + 1.0) + "," + n + " @@") + "\n"
            bind q = 0.0
            while q < n {
                bind out = out + cx_good("+ " + list_get(lb, bpos + q)) + "\n"
                bind q = q + 1.0
            }
            bind bpos = bpos + n
        }
        bind k = k + 2.0
    }
    return out
}

← Back to index