feat(tooling): JetBrains IDE support, LSP navigation, barrel imports, package scripts and hooks

- JetBrains plugin 1.4.0: semantic colours (builtin / vendor / own), template strings,
  brace handling, run configurations and a test console, package.ludic and
  package.lock.ludic editing (completion, docs, app preview, colour previews, asset
  navigation), External Libraries for the runtime and packages, doc pages for built-ins
- ludic-lsp: go to definition for imports, document links, hover with inferred types,
  type definition, signature help with parameters, docs from docs/language
- `import "dir"` resolves a barrel `dir/index.ludic`
- package.ludic `entry`, `script` and `hook before|after <command>`; `ludic <script>`,
  `ludic script`, `ludic scripts`
- `ludic test --verbose` and `--test NAME`; the test runner filters by name

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-16 16:00:28 +03:00
parent da57b20156
commit f92d7f89c6
75 changed files with 46802 additions and 40343 deletions

View file

@ -33,8 +33,11 @@ property Manifest {
provides: []pointer, # the Foo.* namespace(s) this package registers
targets: []pointer, # prebuilt: the targets it ships (native-arm64, wasm32, …)
deps: []Dep,
entry: pointer = "", # `entry "src/game.ludic"` — the program run/build/bundle compile
packs: []pointer, # `pack "<dir>"` — asset roots that go into the .lpak
app: []pointer # `app <key> "<value>"` — flattened key, value, key, value…
app: []pointer, # `app <key> "<value>"` — flattened key, value, key, value…
scripts: []pointer, # `script "<name>" "<command>"` — flattened name, command, …
hooks: []pointer # `hook <before|after> <event> "<command>"` — flattened triples
}
function manifest_new() -> Manifest {
@ -43,11 +46,14 @@ function manifest_new() -> Manifest {
m.ver = ""
m.kind = "source"
m.hash = ""
m.entry = ""
m.provides = new []pointer
m.targets = new []pointer
m.deps = new []Dep
m.packs = new []pointer
m.app = new []pointer
m.scripts = new []pointer
m.hooks = new []pointer
return m
}
@ -114,11 +120,18 @@ function tok_line(line: pointer) -> []pointer {
let c = line[i]
if c == '#' { i = n } # '#' comment
else if c == ' ' or c == '\t' { i += 1 } # whitespace
else if c == '"' { # "quoted"
else if c == '"' { # "quoted", with \" and \\ escapes
i += 1
let start = i
while i < n and line[i] != '"' { i += 1 }
push(toks, sslice(line, start, i))
var word = ""
var start = i
while i < n and line[i] != '"' {
if line[i] == 92 and i + 1 < n {
word = word + sslice(line, start, i) + sslice(line, i + 1, i + 2)
i += 2
start = i
} else { i += 1 }
}
push(toks, word + sslice(line, start, i))
if i < n { i += 1 }
} else { # bare word
let start = i
@ -158,6 +171,7 @@ function parse_manifest(text: pointer) -> Manifest {
if head == "package" { if len(ts) > 1 { m.module = ts[1] } }
else if head == "version" { if len(ts) > 1 { m.ver = ts[1] } }
else if head == "kind" { if len(ts) > 1 { m.kind = ts[1] } }
else if head == "entry" { if len(ts) > 1 { m.entry = ts[1] } }
else if head == "provides" { var k = 1; while k < len(ts) { push(m.provides, ts[k]); k += 1 } }
else if head == "targets" { var k = 1; while k < len(ts) { push(m.targets, ts[k]); k += 1 } }
else if head == "require" {
@ -169,6 +183,12 @@ function parse_manifest(text: pointer) -> Manifest {
# `app name "Maroon Lake"` — the metadata a macOS bundle is built from.
# Held as a flat key/value list rather than a property per key, so a new
# Info.plist field is a line in the bundler and nothing here.
else if head == "script" {
if len(ts) > 2 { push(m.scripts, ts[1]); push(m.scripts, ts[2]) }
}
else if head == "hook" {
if len(ts) > 3 { push(m.hooks, ts[1]); push(m.hooks, ts[2]); push(m.hooks, ts[3]) }
}
else if head == "app" {
if len(ts) > 2 { push(m.app, ts[1]); push(m.app, ts[2]) }
}