ludic fmt for editors: --lint --json, and a buffer on stdin (R9)
ludic-fmt --lint --json prints the violations --lint reports as one JSON array on stdout,
[{file, line, col, rule, message}] ordered by file, line and column (col where the rule knows it),
the summary on stderr, --lint's exit status, and never rewrites the baseline. ludic-fmt - refuses a
buffer that does not read as Ludic (a string/template/key literal left open, a bracket never closed
or closed by the wrong one) with exit 2 and name:line:col on stderr; - --lint judges a buffer as the
file --stdin-name names (--stdin-rel: that path relative to the project), against its baseline and
lint paths. ludic fmt --lint and ludic fmt - run it from the nearest package.ludic upwards (from
--stdin-name's directory when given). Hooks read nothing and write to stderr under --json or -.
Regression cases added to test-tools and ludic-dev test (fmt_editor_cases), not run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c804c4b0ee
commit
047bdf4189
13 changed files with 405 additions and 17 deletions
|
|
@ -123,6 +123,28 @@ function stdin_file_cases() -> void {
|
|||
ok(lbl)
|
||||
}
|
||||
|
||||
# `ludic fmt` for editors (R9): run from a directory under the project, `--lint --json` and `-` find
|
||||
# the nearest package.ludic upwards and judge a buffer as the file --stdin-name names, relative to it
|
||||
function fmt_editor_cases() -> void {
|
||||
let lbl = "ludic fmt --lint --json and ludic fmt - from inside a project"
|
||||
let p = `{tmp_dir()}/fmt_ed`
|
||||
let here = capture_line("pwd")
|
||||
shell(`rm -rf {p} && mkdir -p {p}/src/deep`)
|
||||
write_file(`{p}/package.ludic`, "package \"t\"\nlint one_statement\nlint paths \"src\"\n")
|
||||
write_file(`{p}/src/deep/a.ludic`, "function f() -> int {\n let a = 1; return a\n}\n")
|
||||
let js = `{p}/out.json`
|
||||
if shq(`cd {p}/src/deep && {here}/bin/ludic fmt --lint --json > {js} 2>/dev/null`) { bad2(lbl, "--lint --json passed a file breaking one_statement"); return }
|
||||
let said = capture(`cat {js}`)
|
||||
if not s_contains(said, `"file": "src/deep/a.ludic", "line": 2, "col": 15`) { bad2(lbl, `--lint --json said {s_trim(said)}`); return }
|
||||
let one = capture(`cd {p}/src/deep && {here}/bin/ludic fmt - --lint --json --stdin-name a.ludic < a.ludic 2>/dev/null`)
|
||||
if not s_contains(one, `"file": "a.ludic", "line": 2`) { bad2(lbl, `a buffer linted as src/deep/a.ludic said {s_trim(one)}`); return }
|
||||
let out = capture(`cd {p}/src/deep && printf 'function g() -> int{{return 2}}\n' | {here}/bin/ludic fmt - --stdin-name {p}/src/deep/b.ludic`)
|
||||
if not (s_trim(out) == "function g() -> int { return 2 }") { bad2(lbl, `ludic fmt - wrote {s_trim(out)}`); return }
|
||||
if shq(`printf '{{\n' | {here}/bin/ludic fmt - > /dev/null 2>&1`) { bad2(lbl, "ludic fmt - accepted an unclosed brace"); return }
|
||||
if shq(`{here}/bin/ludic fmt --json > /dev/null 2>&1`) { bad2(lbl, "--json without --lint was accepted"); return }
|
||||
ok(lbl)
|
||||
}
|
||||
|
||||
# a feature example that consumes an in-repo controller package (packages/): same
|
||||
# as feat_case, but the compiler is pointed at packages/ via LUDIC_MODULES so a
|
||||
# `import "ludic.foo/bar.ludic"` resolves, and LUDIC_HOME anchors the engine
|
||||
|
|
@ -1252,6 +1274,7 @@ function cmd_dev_test() -> int {
|
|||
diag_json_case("rejected/ref_unknown", 1, "@Ref(Shops): this program has no Shops - left unresolved", "--diagnostics=json: an @Ref to a registry the program lacks is a warning beside the error")
|
||||
diag_json_case("rejected/node_bad", 8, "crate.shape is \\\"cube\\\", and field Tool.shape is @OneOf(", "--diagnostics=json: every @Node / @Clip / @Material with no model field, @OneOf of the wrong kind or a row outside its words, and the targets the program lacks as warnings")
|
||||
stdin_file_cases()
|
||||
fmt_editor_cases()
|
||||
feat_case("lang/generics", "", "7 Crater Lake a 2 west 7 0.75 2.5 1", "generics.ludic (L5: generic records and functions, nested instances, fn-typed parameters and fields, inference from the result's slot)")
|
||||
reject_case("rejected/generic_unbound", "cannot tell what T is in this call to pool_new", "a call whose type arguments nothing decides is refused")
|
||||
reject_case("rejected/generic_mismatch", "words wants a Pool<string> and this is a Pool<int>", "two instances of one generic are two types")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue