灵符lingfu
L

lingcoder

↓ 0 downloads

LingCoder: a Geany / VS Code-style IDE for Ling, written in Ling. Explorer, tabs, syntax-coloured editor with lint, find in files, command palette, run/build panel, and hot-reloaded extensions - including a Claude assistant.

Keywords ide, editor, ling, claude, extensions
lingfu install lingcoder
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (34)

ling/ds.ling
# ds.ling - the Ling design-system line work: outline + crosshatch, square corners, tile lettering.
# Colour keys: asm red = sources   bind yellow = presets   ctl green = time   call blue = in/out
#              func indigo = transforms   mod violet = identity   ink grey = structure

fn ds_col(key) {
    if key == "asm" { return c_mix(c_rgb(229.0, 56.0, 59.0), 16777215.0, 0.22) }
    if key == "lit" { return c_mix(c_rgb(247.0, 127.0, 0.0), 16777215.0, 0.22) }
    if key == "bind" { return c_mix(c_rgb(209.0, 155.0, 0.0), 16777215.0, 0.22) }
    if key == "ctl" { return c_mix(c_rgb(42.0, 157.0, 73.0), 16777215.0, 0.22) }
    if key == "call" { return c_mix(c_rgb(31.0, 111.0, 214.0), 16777215.0, 0.22) }
    if key == "func" { return c_mix(c_rgb(75.0, 63.0, 196.0), 16777215.0, 0.22) }
    if key == "mod" { return c_mix(c_rgb(155.0, 63.0, 201.0), 16777215.0, 0.22) }
    return c_mix(c_rgb(86.0, 101.0, 122.0), 16777215.0, 0.22)
}

# clip to a rectangle without losing the surrounding scroll clip
fn ds_clip_to(x, y, w, h) {
    bind x0 = x
    bind y0 = y
    bind x1 = x + w
    bind y1 = y + h
    if st_get("clip_on", 0.0) > 0.5 {
        bind x0 = max(x0, st_get("clip_x0", 0.0))
        bind y0 = max(y0, st_get("clip_y0", 0.0))
        bind x1 = min(x1, st_get("clip_x1", 100000.0))
        bind y1 = min(y1, st_get("clip_y1", 100000.0))
    }
    px_clip(x0, y0, max(0.0, x1 - x0), max(0.0, y1 - y0))
}
fn ds_clip_end() {
    if st_get("clip_on", 0.0) > 0.5 {
        px_clip(st_get("clip_x0", 0.0), st_get("clip_y0", 0.0), st_get("clip_x1", 0.0) - st_get("clip_x0", 0.0), st_get("clip_y1", 0.0) - st_get("clip_y0", 0.0))
    } else { px_unclip() }
}

# parallel hatch lines at `ang` degrees, `sp` px apart, clipped to the box
fn ds_hatch(x, y, w, h, col, ang, sp, a) {
    if w < 1.0 || h < 1.0 { return 0.0 }
    # native: parallel 1 px lines across the box at `ang` degrees, `sp` apart (a script loop of px_line calls cost ~5 ms a frame)
    px_hatch(x, y, w, h, col, ang, max(1.0, sp), a)
}
fn ds_cross(x, y, w, h, col, sp, a) {
    ds_hatch(x, y, w, h, col, 45.0, sp, a)
    ds_hatch(x, y, w, h, col, 135.0, sp, a)
}

# corner brackets
fn ds_brackets(x, y, w, h, n, col) {
    px_hline(x, y, n, col)
    px_vline(x, y, n, col)
    px_hline(x + w - n, y, n, col)
    px_vline(x + w - 1.0, y, n, col)
    px_hline(x, y + h - 1.0, n, col)
    px_vline(x, y + h - n, n, col)
    px_hline(x + w - n, y + h - 1.0, n, col)
    px_vline(x + w - 1.0, y + h - n, n, col)
}

# one letter in a square tile
fn ds_tile(x, y, s, ch, col, on) {
    px_outline(x, y, s, s, col, 0.55)
    # the corner brackets only where there is room for them next to the letter
    if s >= 16.0 { ds_brackets(x, y, s, s, max(2.0, floor(s * 0.28)), col) }
    # a capital fills the square best; drawn large and crisp (edges sharpened) so it reads at small sizes
    bind up = str_upper(ch)
    # assets/font.ttf: capitals start 0.22 em below the origin and are 0.66 em tall - sized to sit inside the tile, 1.5 px clear
    bind fs = max(6.0, (s - 3.0) / 0.66)
    bind gw = g_text_w(fs, up)
    if gw > s - 2.0 { bind fs = fs * (s - 2.0) / gw  bind gw = s - 2.0 }
    bind txt = sel(on, 0.0, c_mix(col, 16777215.0, 0.25))
    px_text(st_get("font", 0.0), x + (s - gw) * 0.5, y + (s - fs * 0.66) * 0.5 - fs * 0.22, fs, txt, up, 1.0)
}
# a word, one letter per tile; `on` = hatched. Returns the width
fn ds_word(x, y, s, text, col, on) {
    bind n = str_len(text)
    # small words: one box around the whole word and bigger letters (a box per letter leaves no room to read them)
    if s < 13.0 { return ds_word_small(x, y, s, text, col, on) }
    bind i = 0.0
    while i < n {
        bind tx = x + i * (s + 1.0)
        if on { ds_hatch(tx + 1.0, y + 1.0, s - 2.0, s - 2.0, col, 45.0, 3.0, 0.7) }
        ds_tile(tx, y, s, str_slice(text, i, 1.0), col, false)
        bind i = i + 1.0
    }
    return n * (s + 1.0)
}
fn ds_word_w(s, text) { return str_len(text) * (s + 1.0) }

# the small form of ds_word: same footprint (a cell of s + 1 per letter), one frame round the word, the letters as large as the
# cells allow (capitals, crisp edges), a little taller than the cell so they read
fn ds_word_small(x, y, s, text, col, on) {
    bind n = str_len(text)
    bind wd = n * (s + 1.0) - 1.0
    bind hh = max(s, 12.0)
    bind top = y + (s - hh) * 0.5
    if on { ds_hatch(x + 1.0, top + 1.0, wd - 2.0, hh - 2.0, col, 45.0, 3.0, 0.6) }
    px_outline(x, top, wd, hh, col, 0.55)
    bind fs = min((hh - 3.0) / 0.66, (s + 0.5) / 0.78)
    bind txt = sel(on, 16777215.0, c_mix(col, 16777215.0, 0.3))
    bind f = st_get("font", 0.0)
    bind i = 0.0
    while i < n {
        bind up = str_upper(str_slice(text, i, 1.0))
        if up != " " {
            bind gw = g_text_w(fs, up)
            px_text(f, x + i * (s + 1.0) + (s - gw) * 0.5, top + (hh - fs * 0.66) * 0.5 - fs * 0.22, fs, txt, up, 1.0)
        }
        bind i = i + 1.0
    }
    return n * (s + 1.0)
}

# double-rule frame with corner brackets and a tabbed title
fn ds_frame(x, y, w, h, col, title) {
    px_outline(x, y, w, h, col, 0.55)
    px_outline(x + 3.0, y + 3.0, w - 6.0, h - 6.0, col, 0.35)
    ds_brackets(x - 1.0, y - 1.0, w + 2.0, h + 2.0, 10.0, col)
    if title != "" {
        bind tw = ds_word_w(12.0, title) + 10.0
        px_rect(x + 14.0, y - 7.0, tw, 14.0, C_BG_PANEL)
        ds_word(x + 19.0, y - 6.0, 12.0, title, col, false)
    }
}

# selector: a row of tile words; returns the new index or -1
fn ds_selector(id, x, y, s, names, cur, col, tip) {
    bind out = 0.0 - 1.0
    bind px = x
    bind i = 0.0
    while i < len(names) {
        bind nm = list_get(names, i)
        bind ww = ds_word_w(s, nm)
        bind on = i == cur
        ds_word(px, y, s, nm, sel(on, col, ds_col("ink")), on)
        if u_hot(px, y, ww, s) {
            if tip != "" { u_tip(tip + ": " + nm) }
            if u_pressed() { bind out = i }
        }
        bind px = px + ww + 7.0
        bind i = i + 1.0
    }
    return out
}
fn ds_selector_w(s, names) {
    bind w = 0.0
    bind i = 0.0
    while i < len(names) {
        bind w = w + ds_word_w(s, list_get(names, i)) + 7.0
        bind i = i + 1.0
    }
    return w
}

# switch: a tile, crosshatched when on, with a tile-word label. Returns the new state (0/1) or -1
fn ds_switch(id, x, y, s, label, on, col, tip) {
    bind out = 0.0 - 1.0
    px_outline(x, y, s, s, sel(on, col, ds_col("ink")), 0.8)
    ds_brackets(x, y, s, s, 4.0, sel(on, col, ds_col("ink")))
    if on { ds_cross(x + 1.0, y + 1.0, s - 2.0, s - 2.0, col, 3.0, 0.7) }
    ds_word(x + s + 5.0, y + (s - 12.0) * 0.5, 12.0, label, sel(on, col, ds_col("ink")), false)
    bind w = s + 8.0 + ds_word_w(12.0, label)
    if u_hot(x, y, w, s) {
        if tip != "" { u_tip(tip + sel(on, " (on)", " (off)")) }
        if u_pressed() { bind out = sel(on, 0.0, 1.0) }
    }
    return out
}

# knob: 270 degree arc, drag vertically (Shift = fine), wheel. Returns the new 0..1 value or -999
fn ds_knob(id, cx, cy, r, label, v, col, readout, tip) {
    bind result = 0.0 - 999.0
    bind rin = r * 0.58
    bind hot = u_hot(cx - r - 3.0, cy - r - 3.0, (r + 3.0) * 2.0, (r + 3.0) * 2.0 + 14.0)
    bind dragging = u_active() == id
    if hot && u_pressed() {
        u_set_active(id)
        st_set("kn_y", u_my())
        st_set("kn_v", v)
        bind dragging = true
    }
    if dragging {
        if u_down() {
            bind sens = sel(u_shift(), 600.0, 150.0)
            bind nv = clampf(st_get("kn_v", v) + (st_get("kn_y", 0.0) - u_my()) / sens, 0.0, 1.0)
            if abs(nv - v) > 0.0005 { bind result = nv }
        } else { u_set_active("")  bind dragging = false }
    } else if hot && u_scroll() != 0.0 && st_get("wheel_used", 0.0) < 0.5 {
        bind result = clampf(v + u_scroll() * sel(u_shift(), 0.004, 0.02), 0.0, 1.0)
        st_set("wheel_used", 1.0)
    }
    if hot && tip != "" { u_tip(tip) }
    bind shown = sel(result > -500.0, result, v)
    bind a0 = 135.0
    bind sw = 270.0 * shown
    bind k = 0.0
    while k < 270.0 {
        bind ang = (a0 + k) * 3.14159265 / 180.0
        bind ang2 = (a0 + k + 15.0) * 3.14159265 / 180.0
        bind swept = k < sw
        px_line(cx + cos(ang) * r, cy + sin(ang) * r, cx + cos(ang2) * r, cy + sin(ang2) * r, col, sel(swept, 0.95, 0.4))
        px_line(cx + cos(ang) * rin, cy + sin(ang) * rin, cx + cos(ang2) * rin, cy + sin(ang2) * rin, col, sel(swept, 0.8, 0.3))
        bind k = k + 15.0
    }
    bind k = 0.0
    while k <= 270.0 {
        bind ang = (a0 + k) * 3.14159265 / 180.0
        if k <= sw {
            px_line(cx + cos(ang) * rin, cy + sin(ang) * rin, cx + cos(ang) * r, cy + sin(ang) * r, col, 0.7)
        } else if k % 20.0 < 0.5 {
            px_line(cx + cos(ang) * rin, cy + sin(ang) * rin, cx + cos(ang) * r, cy + sin(ang) * r, col, 0.3)
        }
        bind k = k + 10.0
    }
    bind ha = (a0 + sw) * 3.14159265 / 180.0
    bind hx = cos(ha)
    bind hy = sin(ha)
    bind tipx = cx + hx * (rin - 1.0)
    bind tipy = cy + hy * (rin - 1.0)
    px_line(cx - hy * 2.5, cy + hx * 2.5, tipx, tipy, col, 0.9)
    px_line(cx + hy * 2.5, cy - hx * 2.5, tipx, tipy, col, 0.9)
    px_ring(cx, cy, 2.5, col, 1.0)
    bind txt = sel(dragging, readout, label)
    bind lw = ds_word_w(9.0, txt)
    ds_word(cx - lw * 0.5, cy + r + 4.0, 9.0, txt, sel(dragging, c_mix(col, 16777215.0, 0.35), col), dragging)
    return result
}

← Back to index