灵符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)

extensions/sample/sample.ling
# sample.ling - the example LingCoder extension: a TODO panel. Shows every hook: _info,
# _commands + _run, _panel_info + _panel, _on_event, and the ide_* API. Every function
# starts with sample_ (the loader enforces it). Edit this file and save: it reloads live.

fn sample_info() { return ["TODO list", "Lists TODO / FIXME lines in the current file; click one to jump to it", "1.0"] }
fn sample_panel_info() { return ["TODO list (sample extension)", "check"] }

fn sample_commands() {
    return ["scan|TODO: Scan Current File||check", "header|TODO: Insert File Header||text"]
}

fn sample_run(id) {
    if id == "scan" {
        sample_scan()
        side_show("ext:sample")
        return num_str(st_llen("smp_line")) + " TODO(s) in " + p_base(ide_cur_path())
    }
    if id == "header" {
        ide_insert("# " + p_base(ide_cur_path()) + " - what this file is for.\n# TODO: write the real header\n")
        return "Header inserted"
    }
    return ""
}

fn sample_on_event(ev, arg) {
    # rescan whenever a file is opened or saved
    if ev == "open" || ev == "save" { st_set("smp_stale", 1.0) }
    return 0.0
}

fn sample_scan() {
    st_lclear("smp_line")
    st_lclear("smp_text")
    st_set("smp_for", ide_cur_path())
    st_set("smp_stale", 0.0)
    bind body = ide_cur_text()
    if body == "" { return 0.0 }
    bind lines = str_split(body, "\n")
    bind i = 0.0
    while i < len(lines) && st_llen("smp_line") < 200.0 {
        bind ln = list_get(lines, i)
        if str_find(ln, "TODO") >= 0.0 || str_find(ln, "FIXME") >= 0.0 || str_find(ln, "XXX") >= 0.0 {
            st_lpush("smp_line", i + 1.0)
            st_lpush("smp_text", str_trim(ln))
        }
        bind i = i + 1.0
    }
    return 0.0
}

fn sample_panel(x, y, w, h) {
    ds_word(x + 10.0, y + 10.0, 10.0, "TODO", ds_col("ctl"), false)
    g_text_r(x + w - 10.0, y + 9.0, 10.0, C_TEXT_DIM, "sample extension")
    if st_get("smp_stale", 1.0) > 0.5 || st_get("smp_for", "") != ide_cur_path() { sample_scan() }
    bind n = st_llen("smp_line")
    bind ry0 = y + 30.0
    bind rh = h - 34.0
    if n < 0.5 {
        g_text_wrap(x + 12.0, ry0 + 6.0, w - 24.0, 11.0, C_TEXT_DIM, "No TODO / FIXME / XXX lines in " + sel(ide_cur_path() == "", "(no file open)", p_base(ide_cur_path())) + ". This panel is extensions/sample/sample.ling - open it to see how extensions work.")
        return 0.0
    }
    bind off = u_scroll_y("smp_sb", x, ry0, w, rh, n * 34.0, st_get("smp_off", 0.0))
    st_set("smp_off", off)
    u_clip(x, ry0, w, rh)
    bind i = 0.0
    while i < n {
        bind ry = ry0 + 2.0 + i * 34.0 - off
        if ry + 34.0 > ry0 && ry < ry0 + rh {
            bind hot = u_hot(x, ry, w - 12.0, 33.0)
            if hot { g_rect(x, ry, w, 33.0, C_HOVER_ROW) }
            icon_draw("check", x + 15.0, ry + 16.0, 10.0, ds_col("ctl"))
            g_text(x + 28.0, ry + 4.0, 11.0, sel(hot, C_TEXT_STRONG, C_TEXT), "line " + num_str(st_lget("smp_line", i)))
            g_text_fit(x + 28.0, ry + 18.0, w - 44.0, 10.0, C_TEXT_DIM, st_lget("smp_text", i))
            if hot && u_pressed() { ide_goto(st_lget("smp_line", i) - 1.0) }
        }
        bind i = i + 1.0
    }
    u_unclip()
    return 0.0
}

← Back to index