fix(dashboard): preserve blank lines and improve Rhai parser errors
Two follow-ups on the Rhai formatter shipped in 0.5.1.
* Formatter no longer collapses user-intent blank lines between
statements. The lexer now records a side-channel list of offsets
where the source contained two-or-more consecutive newlines; the
formatter consults it and emits a single blank in the same spot
(rustfmt's `blank_lines_upper_bound = 1` policy applied strictly —
the prior forced blank between top-level `fn` decls is dropped, so
the formatter never *adds* a blank the user didn't write).
* Parse errors now read like Rhai's own diagnostics. `expect()` takes
an optional `role` hint and each call site supplies a domain phrase
(`name of a variable`, `function name in function declaration`,
`'{' to begin a block`, `name of a property`, …). End-of-input is
reported as `script is incomplete`. The dashboard banner renders
`Parse error: {message} (line L, position C)` with 1-based
coordinates, matching Rhai's format exactly.
The FormatError payload also keeps the byte `offset` so callers that
want to drive the editor cursor (CodeMirror works in offsets) still
have it.
Also folds the workspace Cargo.lock version bumps for 0.5.1 — the
lock-file rewrite that should have travelled with the prior commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,11 +22,10 @@ describe('format — basic shape', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('separates top-level fn decls with a blank line', () => {
|
||||
it('does not insert a blank between fn decls the user did not separate', () => {
|
||||
// Strict preserve-only policy: no source blank => no emitted blank.
|
||||
const out = formatted('fn a(){1}fn b(){2}');
|
||||
expect(out).toBe(
|
||||
'fn a() {\n\t1\n}\n\nfn b() {\n\t2\n}\n'
|
||||
);
|
||||
expect(out).toBe('fn a() {\n\t1\n}\nfn b() {\n\t2\n}\n');
|
||||
});
|
||||
|
||||
it('renders if / else if / else with blocks', () => {
|
||||
@@ -87,16 +86,48 @@ describe('format — reflow', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('format — blank-line preservation', () => {
|
||||
it('preserves a single blank line between statements', () => {
|
||||
const src = 'let a = 1;\n\nlet b = 2;';
|
||||
expect(formatted(src)).toBe('let a = 1;\n\nlet b = 2;\n');
|
||||
});
|
||||
|
||||
it('collapses multiple blank lines to a single one', () => {
|
||||
const src = 'let a = 1;\n\n\n\nlet b = 2;';
|
||||
expect(formatted(src)).toBe('let a = 1;\n\nlet b = 2;\n');
|
||||
});
|
||||
|
||||
it('preserves blanks inside block bodies', () => {
|
||||
const src = 'fn process() {\n\tlet a = 1;\n\n\tlet b = 2;\n}';
|
||||
expect(formatted(src)).toBe('fn process() {\n\tlet a = 1;\n\n\tlet b = 2;\n}\n');
|
||||
});
|
||||
|
||||
it('does not invent blanks between adjacent statements', () => {
|
||||
expect(formatted('let a=1;let b=2;')).toBe('let a = 1;\nlet b = 2;\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('format — parse failures', () => {
|
||||
it('returns ok=false with the first parse error', () => {
|
||||
const r = format('let = ;');
|
||||
it('returns ok=false with a Rhai-flavored message and 1-based line/column', () => {
|
||||
// Pattern from the user complaint: `let;` should surface as
|
||||
// "Expecting name of a variable" at line/column.
|
||||
const r = format('let msg = ctx.request.params.name;\nlet;\n');
|
||||
expect(r.ok).toBe(false);
|
||||
if (!r.ok) {
|
||||
expect(typeof r.error.message).toBe('string');
|
||||
expect(r.error.message).toBe('Expecting name of a variable');
|
||||
expect(r.error.line).toBe(2);
|
||||
expect(r.error.column).toBe(4);
|
||||
expect(r.error.offset).toBeGreaterThanOrEqual(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('reports script-incomplete on truncated input', () => {
|
||||
// `fn` alone — the parser expects a function name and hits EOF.
|
||||
const r = format('fn');
|
||||
expect(r.ok).toBe(false);
|
||||
if (!r.ok) expect(r.error.message).toMatch(/script is incomplete/i);
|
||||
});
|
||||
|
||||
it('does not partially rewrite when parsing fails', () => {
|
||||
const r = format('let x = 1; this is garbage');
|
||||
expect(r.ok).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user