L
lingcoder
↓ 0 downloadsLingCoder: 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 lingcoderCode / Files (34)
ling/palette.ling
# palette.ling - the command palette (Ctrl+Shift+P) and Go to File (Ctrl+P). One modal,
# two modes (st "pal_mode"): "cmd" lists every menu command (+ extension commands);
# "file" lists the project files and the open buffers. In file mode ":12" or "name:12"
# jumps to a line.
fn pal_build_cmds() {
bind out = []
bind seen = "|"
bind titles = menu_titles()
bind m = 0.0
while m < len(titles) {
bind items = menu_def(m)
bind i = 0.0
while i < len(items) {
bind it = list_get(items, i)
if it != "-" {
bind act = menu_field(it, 1)
if !str_starts_with(act, "sub:") && act != "none" && str_find(seen, "|" + act + "|") < 0.0 {
bind out = list_push(out, menu_field(it, 0) + "|" + act + "|" + menu_field(it, 2) + "|" + menu_field(it, 3) + "|" + list_get(titles, m))
bind seen = seen + act + "|"
}
}
bind i = i + 1.0
}
bind m = m + 1.0
}
return out
}
fn pal_build_files() {
bind out = []
# open buffers first
bind i = 0.0
while i < buf_count() {
bind p = buf_path(buf_at(i))
bind out = list_push(out, p_base(p) + "|open:" + num_str(buf_at(i)) + "|open|" + ext_icon(p_ext(p)) + "|" + p_rel(proj_root(), p))
bind i = i + 1.0
}
bind n = proj_files()
bind k = 0.0
while k < n {
bind rel = st_lget("proj_files", k)
bind out = list_push(out, p_base(rel) + "|file:" + rel + "||" + ext_icon(p_ext(rel)) + "|" + rel)
bind k = k + 1.0
}
return out
}
fn pal_open(mode) {
st_set("pal_mode", mode)
if mode == "cmd" { st_set("pal_all", pal_build_cmds()) }
else { st_set("pal_all", pal_build_files()) }
st_set("pal_q", "")
st_set("pal_q_last", "-")
st_set("pal_sel", 0.0)
st_set("pal_off", 0.0)
st_set("menu_open", 0.0 - 1.0)
modal_open("palette")
st_set("focus", "pal_q")
st_set("text_owner", "")
}
# fuzzy score: substring beats word start beats scattered letters; -1 = no match
fn pal_score(q, t) {
if q == "" { return 0.0 }
bind l = str_lower(t)
bind at = str_find(l, q)
if at >= 0.0 {
bind word = true
if at > 0.5 { bind word = str_slice(l, at - 1.0, 1.0) == " " || str_slice(l, at - 1.0, 1.0) == "/" }
return 1000.0 - at + sel(word, 200.0, 0.0)
}
bind ws = str_split(q, " ")
if len(ws) > 1.5 {
bind all = true
bind wi = 0.0
while wi < len(ws) {
if list_get(ws, wi) != "" && str_find(l, list_get(ws, wi)) < 0.0 { bind all = false }
bind wi = wi + 1.0
}
if all { return 600.0 }
}
bind qi = 0.0
bind ti = 0.0
bind gaps = 0.0
bind last = 0.0 - 1.0
bind nq = str_len(q)
bind nt = str_len(l)
while qi < nq && ti < nt {
if str_slice(l, ti, 1.0) == str_slice(q, qi, 1.0) {
if last >= 0.0 { bind gaps = gaps + (ti - last - 1.0) }
bind last = ti
bind qi = qi + 1.0
}
bind ti = ti + 1.0
}
if qi < nq { return 0.0 - 1.0 }
return max(1.0, 300.0 - gaps * 4.0)
}
fn pal_list_insert(l, pos, v) {
bind out = []
bind i = 0.0
while i < len(l) {
if i == pos { bind out = list_push(out, v) }
bind out = list_push(out, list_get(l, i))
bind i = i + 1.0
}
if pos >= len(l) { bind out = list_push(out, v) }
return out
}
fn pal_filter(q) {
bind all = st_get("pal_all", [])
bind n = len(all)
bind idx = []
bind sc = []
bind recent = st_get("pal_recent", "")
bind i = 0.0
while i < n {
bind it = list_get(all, i)
bind s = pal_score(q, menu_field(it, 0) + " " + menu_field(it, 4))
if q == "" {
bind ra = str_find(recent, "|" + menu_field(it, 1) + "|")
bind s = sel(ra >= 0.0, 5000.0 - ra, 0.0 - i * 0.001)
}
if s > 0.0 - 0.5 || q == "" {
bind pos = len(idx)
bind more = pos > 0.5
while more {
if list_get(sc, pos - 1.0) < s { bind pos = pos - 1.0 } else { bind more = false }
if pos < 0.5 { bind more = false }
}
bind idx = pal_list_insert(idx, pos, i)
bind sc = pal_list_insert(sc, pos, s)
}
bind i = i + 1.0
}
return idx
}
fn pal_run(act, lineafter) {
modal_close()
st_set("pal_recent", "|" + act + str_replace(st_get("pal_recent", "|"), "|" + act + "|", "|"))
if str_starts_with(act, "open:") {
buf_switch_to(to_number(str_slice(act, 5.0, str_len(act) - 5.0)))
if lineafter >= 0.0 { ed_goto_line(buf_cur(), lineafter) }
return 0.0
}
if str_starts_with(act, "file:") {
bind bi = buf_open(proj_abs(str_slice(act, 5.0, str_len(act) - 5.0)))
if bi >= 0.0 && lineafter >= 0.0 { ed_goto_line(bi, lineafter) }
return 0.0
}
do_action(act)
return 0.0
}
fn modal_palette() {
bind W = get_width()
bind H = get_height()
bind w = min(620.0, W - 40.0)
bind rh = 28.0
bind rows = clampf(floor((H - 200.0) / rh), 4.0, 12.0)
bind h = 58.0 + rows * rh + 28.0
bind x = floor((W - w) * 0.5)
bind y = 56.0
g_rect_a(0.0, 0.0, W, H, 0.0, 0.45)
px_rrect(x + 3.0, y + 5.0, w, h, 8.0, 0.0, 0.5)
g_rrect(x, y, w, h, 7.0, C_POPUP)
g_rframe(x, y, w, h, 7.0, C_POPUP_LINE)
bind mode = st_get("pal_mode", "cmd")
icon_draw(sel(mode == "cmd", "terminal", "search"), x + 22.0, y + 25.0, 13.0, C_TEXT_DIM)
bind q = u_textfield("pal_q", x + 38.0, y + 12.0, w - 50.0, 28.0, st_get("pal_q", ""))
st_set("pal_q", q)
st_set("focus", "pal_q")
if q == "" {
g_text(x + 46.0, y + 19.0, 12.0, C_TEXT_DIM, sel(mode == "cmd", "Type a command: save, run, find, claude ...", "Type a file name (add :12 to jump to a line)"))
}
# a trailing :NN in file mode jumps to that line
bind lineafter = 0.0 - 1.0
bind ql = str_lower(str_trim(q))
if mode == "file" {
bind cp = str_find(ql, ":")
if cp >= 0.0 {
bind lineafter = to_number(str_slice(ql, cp + 1.0, str_len(ql) - cp - 1.0)) - 1.0
bind ql = str_trim(str_slice(ql, 0.0, cp))
if ql == "" && lineafter >= 0.0 {
# ":12" alone: jump in the current file
g_text(x + 46.0, y + 19.0, 12.0, C_TEXT_DIM, "line " + num_str(lineafter + 1.0) + " of the current file - Enter jumps")
}
}
}
if ql != st_get("pal_q_last", "-") {
st_set("pal_res", pal_filter(ql))
st_set("pal_q_last", ql)
st_set("pal_sel", 0.0)
st_set("pal_off", 0.0)
}
bind res = st_get("pal_res", [])
bind all = st_get("pal_all", [])
bind n = len(res)
bind cur = clampf(st_get("pal_sel", 0.0), 0.0, max(0.0, n - 1.0))
if key_pressed("down") { bind cur = min(n - 1.0, cur + 1.0) }
if key_pressed("up") { bind cur = max(0.0, cur - 1.0) }
if key_pressed("pagedown") { bind cur = min(n - 1.0, cur + rows) }
if key_pressed("pageup") { bind cur = max(0.0, cur - rows) }
bind off = st_get("pal_off", 0.0)
if u_in(x, y + 50.0, w, rows * rh) && u_scroll() != 0.0 {
bind off = clampf(off - u_scroll() * 3.0, 0.0, max(0.0, n - rows))
}
if cur < off { bind off = cur }
if cur >= off + rows { bind off = cur - rows + 1.0 }
bind off = clampf(off, 0.0, max(0.0, n - rows))
bind ly = y + 50.0
u_hsep(x + 8.0, ly - 2.0, w - 16.0)
bind run = ""
bind r = 0.0
while r < rows {
bind k = off + r
if k < n {
bind it = list_get(all, list_get(res, k))
bind ry = ly + r * rh
bind hot = u_hot(x + 4.0, ry, w - 8.0, rh)
if hot && (u_mx() != st_get("pal_mx", 0.0) || u_my() != st_get("pal_my", 0.0)) { bind cur = k }
if k == cur {
g_rrect_a(x + 6.0, ry + 1.0, w - 12.0, rh - 2.0, 4.0, C_BLUE, 0.28)
g_rect(x + 6.0, ry + 4.0, 3.0, rh - 8.0, C_BLUE)
}
bind ic = menu_field(it, 3)
if ic != "" { icon_draw(ic, x + 26.0, ry + rh * 0.5, 12.0, sel(k == cur, C_TEXT_STRONG, C_TEXT_DIM)) }
bind sc = menu_field(it, 2)
bind scw = sel(sc == "", 0.0, g_text_w(11.0, sc) + 16.0)
bind lab = menu_field(it, 0)
bind lw = g_text_w(13.0, lab)
bind room = w - 60.0 - scw - 8.0
g_text_fit(x + 44.0, ry + 7.0, room, 13.0, sel(k == cur, C_TEXT_STRONG, C_TEXT), lab)
if lw + 20.0 < room { g_text_fit(x + 44.0 + lw + 12.0, ry + 8.0, room - lw - 12.0, 11.0, C_TEXT_DIM, menu_field(it, 4)) }
if sc != "" {
bind sx = x + w - 12.0 - scw + 4.0
g_rrect(sx, ry + 5.0, scw - 6.0, rh - 10.0, 3.0, C_BG_DEEP)
g_text_c(sx + (scw - 6.0) * 0.5, ry + 8.0, 11.0, C_TEXT, sc)
}
if hot && u_pressed() { bind run = menu_field(it, 1) }
}
bind r = r + 1.0
}
st_set("pal_mx", u_mx())
st_set("pal_my", u_my())
if n < 0.5 { g_text_c(x + w * 0.5, ly + 16.0, 12.0, C_TEXT_DIM, sel(mode == "cmd", "No command matches", "No file matches")) }
st_set("pal_sel", cur)
st_set("pal_off", off)
g_text(x + 14.0, y + h - 22.0, 11.0, C_TEXT_DIM, "Up / Down choose Enter run Esc close")
g_text_r(x + w - 14.0, y + h - 22.0, 11.0, C_TEXT_DIM, num_str(n) + sel(mode == "cmd", " commands", " files"))
if key_pressed("enter") {
if mode == "file" && str_trim(str_lower(q)) != "" && str_starts_with(str_trim(str_lower(q)), ":") && lineafter >= 0.0 {
modal_close()
ed_goto_line(buf_cur(), lineafter)
return 0.0
}
if n > 0.5 { bind run = menu_field(list_get(all, list_get(res, cur)), 1) }
}
if u_pressed() && !u_in(x, y, w, h) { modal_close() }
if run != "" { pal_run(run, lineafter) }
return 0.0
}