Editor support generalized: shared core, language server, Vim and VS Code (from dev eb5baf9cbe59)
doc/edit/ now holds a shared Python implementation of the language's structural layer (klammertext_edit.py) and a dependency-free language server (klammertext_ls.py), with integrations for Emacs, Sublime Text, Vim, and Visual Studio Code. The editor test suite in tst/ covers the core's API and CLI, the language server protocol, the VS Code extension, headless Vim, and Emacs byte-equality. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,45 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Drive the Sublime Text editor cores over fixture files, outside Sublime.
|
||||
"""Drive the shared editor core over fixture files.
|
||||
|
||||
Usage: editor_driver.py SUBLIME_DIR (MODE INFILE OUTFILE)...
|
||||
Usage: editor_driver.py SHARED_DIR (MODE INFILE OUTFILE)...
|
||||
|
||||
MODE is `indent` or `align`; each triple applies that tool to INFILE and
|
||||
writes the result to OUTFILE. The Sublime plugin files import without the
|
||||
`sublime` module (their try/except guard), so the pure cores run under plain
|
||||
python3. Also asserts the built-in error path (no enclosing table).
|
||||
MODE is `indent`, `align` (call the shared core's API), or `indent-cli`,
|
||||
`align-cli` (run the same operation through the core's command-line
|
||||
interface, as the Vim plugin does). Each triple applies that tool to INFILE
|
||||
and writes the result to OUTFILE. Also asserts the built-in error paths
|
||||
(no enclosing table; empty `check` output on balanced fixtures).
|
||||
Called by editor_test.sh; exits nonzero on an internal error.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def apply_indent(KI, s):
|
||||
bols = [0] + [i + 1 for i, ch in enumerate(s)
|
||||
if ch == '\n' and i + 1 < len(s)]
|
||||
out = s
|
||||
for a, b, new in sorted(KI.reindent_lines(s, bols), reverse=True):
|
||||
out = out[:a] + new + out[b:]
|
||||
return out
|
||||
def apply_indent(KE, s):
|
||||
return KE.indent_text(s)
|
||||
|
||||
|
||||
def apply_align(KA, s):
|
||||
def apply_align(KE, s):
|
||||
caret = s.index('|') if '|' in s else 0
|
||||
span = KA.enclosing_span(s, caret, KA.ALIGN_KLAMMERS)
|
||||
if span is None:
|
||||
return s
|
||||
_name, cs, ce = span
|
||||
edits, _msg = KA.compute_edits(s[cs:ce])
|
||||
out = s
|
||||
for a, b, new in sorted(edits, reverse=True):
|
||||
out = out[:cs + a] + new + out[cs + b:]
|
||||
return out
|
||||
return KE.align_text(s, caret)[0]
|
||||
|
||||
|
||||
def line_col_of_first_bar(s):
|
||||
pos = s.index('|') if '|' in s else 0
|
||||
line = s.count('\n', 0, pos) + 1
|
||||
col = pos - (s.rfind('\n', 0, pos) + 1) + 1
|
||||
return line, col
|
||||
|
||||
|
||||
def run_cli(script, args, s):
|
||||
r = subprocess.run([sys.executable, script] + args,
|
||||
input=s.encode('utf-8'), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
if r.returncode != 0:
|
||||
sys.exit("editor_driver.py: CLI failed: %s" % r.stderr.decode())
|
||||
return r.stdout.decode('utf-8')
|
||||
|
||||
|
||||
def main():
|
||||
sublime_dir = sys.argv[1]
|
||||
sys.path.insert(0, sublime_dir)
|
||||
import Klammertext_indent as KI
|
||||
import Klammertext_align as KA
|
||||
shared_dir = sys.argv[1]
|
||||
sys.path.insert(0, shared_dir)
|
||||
import klammertext_edit as KE
|
||||
script = shared_dir + '/klammertext_edit.py'
|
||||
|
||||
args = sys.argv[2:]
|
||||
for k in range(0, len(args), 3):
|
||||
@@ -47,16 +52,27 @@ def main():
|
||||
with open(infile) as f:
|
||||
s = f.read()
|
||||
if mode == 'indent':
|
||||
out = apply_indent(KI, s)
|
||||
out = apply_indent(KE, s)
|
||||
elif mode == 'align':
|
||||
out = apply_align(KA, s)
|
||||
out = apply_align(KE, s)
|
||||
elif mode == 'indent-cli':
|
||||
out = run_cli(script, ['indent'], s)
|
||||
elif mode == 'align-cli':
|
||||
line, col = line_col_of_first_bar(s)
|
||||
out = run_cli(script, ['align', str(line), str(col)], s)
|
||||
else:
|
||||
sys.exit("editor_driver.py: unknown mode: " + mode)
|
||||
with open(outfile, 'w') as f:
|
||||
f.write(out)
|
||||
|
||||
# Error path: no enclosing table klammer.
|
||||
assert KA.enclosing_span("no table here\n", 3, KA.ALIGN_KLAMMERS) is None
|
||||
# Error and diagnostics paths.
|
||||
assert KE.enclosing_span("no table here\n", 3, KE.ALIGN_KLAMMERS) is None
|
||||
assert KE.diagnostics("@i abc @ #[ x ]# ^@\n") == []
|
||||
probs = KE.diagnostics("@i abc\n@ol x ul@\n")
|
||||
assert any('never closed' in p['message'] for p in probs), probs
|
||||
assert any('ul@' in p['message'] for p in probs), probs
|
||||
assert run_cli(script, ['check'], "@i abc @\n") == ""
|
||||
assert '1:1:' in run_cli(script, ['check'], "@i abc\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user