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/plat_desktop.ling
# plat_desktop.ling - what the desktop runtime (ling run / ling build win|lin|mac|pios) can do.
# Every plat_* function has a twin in plat_os.ling with the same name and arguments, so the IDE
# calls these and never a native builtin that one runtime lacks.
fn plat_name() { return "desktop" }
fn plat_can_write() { return true }
fn plat_has_dialogs() { return true }
# ── files ────────────────────────────────────────────────────────────────────
fn plat_reveal(path) { fs_reveal(path) }
fn plat_dlg_open(title, exts, start) { return dlg_open(title, exts, start) }
fn plat_dlg_save(title, exts, name, start) { return dlg_save(title, exts, name, start) }
fn plat_dlg_folder(title, start) { return dlg_folder(title, start) }
fn plat_os_name() { return tm_sys_os() }
fn plat_arch() { return tm_sys_arch() }
fn plat_args() { return get_args() }
fn plat_cwd() { return fs_cwd() }
fn plat_home() { return fs_home() }
fn plat_mkdir(p) { return fs_mkdir(p) }
fn plat_copy(a, b) { return fs_copy(a, b) }
fn plat_delete(p) { return fs_delete(p) }
fn plat_rename(a, b) { return rename_file(a, b) }
fn plat_list(p) { return fs_list(p) }
fn plat_is_dir(p) { return fs_is_dir(p) }
fn plat_write(path, text) { write_file(path, text) return true }
fn plat_read(path) {
if !file_exists(path) { return "" }
return read_file(path)
}
fn plat_file_size(p) { return file_size(p) }
fn plat_file_modified(p) { return file_modified(p) }
fn plat_screenshot(name) { screenshot(name) }
fn plat_close() { close_window() }
fn plat_clip_get() { return clipboard_get() }
fn plat_clip_set(s) { return clipboard_set(s) }
fn plat_env(name, d) { return env_get(name, d) }
# run a generated .bat / .sh in a terminal window the user can watch
fn plat_launch(path) {
bind ext = str_lower(p_ext(path))
if ext == "bat" || ext == "sh" || ext == "cmd" { return tm_sys_run(path) }
fs_reveal(path)
return true
}
# ── background jobs ───────────────────────────────────────────────────────────
# Preferred: the proc_* builtins (ling built/packed with the `web` feature) run a command
# silently with stdout+stderr appended to a log file. Fallback: a wrapper script started
# with tm_sys_run in a visible terminal; the wrapper appends ___LINGCODER_DONE___ <code>
# to the log so job polling can see the end. Probe once with try().
fn plat_probe_proc() { return proc_start("", [], "") }
fn plat_has_proc() {
if st_has("has_proc") > 0.5 { return st_get("has_proc", 0.0) > 0.5 }
bind r = try(plat_probe_proc)
bind v = 0.0
if r { bind v = 1.0 }
st_set("has_proc", v)
return v > 0.5
}
# start `exe` with list `argv` in `dir`; output appends to `log`. Returns a job handle
# (>= 1000 = proc id + 1000, 1 = wrapper script started, -1 = failed).
fn plat_job_start(exe, argv, dir, log) {
plat_write(log, "")
if plat_has_proc() {
# proc_start has no working directory: wrap the command so `cd` happens inside
bind id = 0.0 - 1.0
if plat_os_name() == "windows" {
bind full = ["/c", "cd /d \"" + str_replace(dir, "/", "\\") + "\" && \"" + str_replace(exe, "/", "\\") + "\""]
bind i = 0.0
while i < len(argv) {
bind full = list_push(full, list_get(argv, i))
bind i = i + 1.0
}
bind id = proc_start("cmd", full, log)
} else {
bind q = "cd \"" + dir + "\" && \"" + exe + "\""
bind i = 0.0
while i < len(argv) {
bind q = q + " \"" + list_get(argv, i) + "\""
bind i = i + 1.0
}
bind id = proc_start("sh", ["-c", q], log)
}
if id < 0.0 { return 0.0 - 1.0 }
return id + 1000.0
}
# wrapper script fallback; the script names derive from the log name so two jobs
# (a build and a Claude request, say) never overwrite each other's wrapper
bind qargs = ""
bind i = 0.0
while i < len(argv) {
bind qargs = qargs + " \"" + list_get(argv, i) + "\""
bind i = i + 1.0
}
return plat_job_script("\"" + str_replace(exe, "/", "\\") + "\"" + qargs, "\"" + exe + "\"" + qargs, dir, log)
}
# run a raw command line (shell syntax) as a background job; same contract as plat_job_start
fn plat_job_shell(cmdline, dir, log) {
plat_write(log, "")
if plat_has_proc() {
bind id = 0.0 - 1.0
if plat_os_name() == "windows" {
bind id = proc_start("cmd", ["/c", "cd /d \"" + str_replace(dir, "/", "\\") + "\" && " + cmdline], log)
} else {
bind id = proc_start("sh", ["-c", "cd \"" + dir + "\" && " + cmdline], log)
}
if id < 0.0 { return 0.0 - 1.0 }
return id + 1000.0
}
return plat_job_script(cmdline, cmdline, dir, log)
}
# shared wrapper-script path: write <log-stem>_job.bat (+ a .vbs shim so no console
# window flashes) or <log-stem>_job.sh, appending output and the done marker to `log`
fn plat_job_script(wincmd, shcmd, dir, log) {
bind stem = p_join(p_dir(log), p_stem(log) + "_job")
if plat_os_name() == "windows" {
bind sp = stem + ".bat"
bind s = "@echo off\r\ncd /d \"" + str_replace(dir, "/", "\\") + "\"\r\n"
bind s = s + wincmd + " >> \"" + str_replace(log, "/", "\\") + "\" 2>&1\r\n"
bind s = s + "echo ___LINGCODER_DONE___ %errorlevel% >> \"" + str_replace(log, "/", "\\") + "\"\r\n"
plat_write(sp, s)
bind vp = stem + ".vbs"
bind v = "CreateObject(\"WScript.Shell\").Run \"\"\"" + str_replace(sp, "/", "\\") + "\"\"\", 0, False\r\n"
plat_write(vp, v)
if tm_sys_run(vp) { return 1.0 }
if tm_sys_run(sp) { return 1.0 }
return 0.0 - 1.0
}
bind sp = stem + ".sh"
bind s = "#!/usr/bin/env sh\ncd \"" + dir + "\"\n"
bind s = s + shcmd + " >> \"" + log + "\" 2>&1\n"
bind s = s + "echo \"___LINGCODER_DONE___ $?\" >> \"" + log + "\"\n"
plat_write(sp, s)
if tm_sys_run(sp) { return 1.0 }
return 0.0 - 1.0
}
# -2 while running, else the exit code
fn plat_job_poll(handle, log) {
if handle >= 1000.0 { return proc_poll(handle - 1000.0) }
bind t = plat_read(log)
bind at = str_find(t, "___LINGCODER_DONE___")
if at < 0.0 { return 0.0 - 2.0 }
bind rest = str_trim(str_slice(t, at + 21.0, 12.0))
bind sp = str_find(rest, "\n")
if sp >= 0.0 { bind rest = str_trim(str_slice(rest, 0.0, sp)) }
return to_number(rest)
}
fn plat_job_kill(handle) {
if handle >= 1000.0 { proc_kill(handle - 1000.0) }
return 0.0
}
# the log text without the wrapper's end marker line
fn plat_job_log(log) {
bind t = plat_read(log)
bind at = str_find(t, "___LINGCODER_DONE___")
if at >= 0.0 { bind t = str_slice(t, 0.0, at) }
return t
}