From c5584fd0790fe864c8615294e876263f2ccddb99 Mon Sep 17 00:00:00 2001 From: Teakowa Date: Mon, 19 Oct 2026 05:35:15 +0800 Subject: [PATCH] fix(provider): count string-entity lexer errors as validateEdits syntaxError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up to #578: invalid-string-entity and unknown-string-entity are lexer verdicts on the document's own text — the entity table is fixed vocabulary needing no project context — so their omission from DOCUMENT_SYNTAX_CODES made an edit introducing "\&bogus;" return valid:true instead of syntaxError. Adds both codes, documents the intentionally deferred code classes on the allowlist, and covers both entity error kinds in the normative-rules test. The new assertions fail without the allowlist entries. --- crates/opy-provider/src/edits.rs | 22 +++++++++++++++++++- crates/opy-provider/tests/lpp.rs | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/crates/opy-provider/src/edits.rs b/crates/opy-provider/src/edits.rs index 1686bdad..30b35d7c 100644 --- a/crates/opy-provider/src/edits.rs +++ b/crates/opy-provider/src/edits.rs @@ -45,7 +45,25 @@ const CROSS_FILE_CODES: [&str; 7] = [ /// reports `syntaxError` only for one of these attributed to the validated /// document itself; every diagnostic whose verdict depends on sibling /// sources or bindings defers to the client's post-edit project `lpp/check`. -const DOCUMENT_SYNTAX_CODES: [&str; 14] = [ +/// +/// Intentionally deferred, so a fragment's own edits stay `valid` here and +/// the project's `lpp/check` arbitrates: +/// - name/binding verdicts (`unknown-*`, `duplicate-*`, `invalid-arity`, +/// `unresolved-reference`, `incompatible-protocol`) — the referent may be +/// declared in a sibling file; +/// - merged-table duplicates (`rule-prefix-template-duplicate`, +/// `replacement-duplicate`, `post-compile-hook-duplicate`, +/// `macro-redeclaration`) — the earlier entry may come from an include; +/// - macro and script verdicts (`macro-arity`, `macro-recursion`, +/// `script-*`) — the macro table and script files come from includes; +/// - external-file verdicts (`include-not-found`, `settings-not-found`, +/// `translations-invalid`, `settings-expression`, +/// `rule-prefix-template-invalid`) — the source text or constants the +/// verdict needs live outside the document; +/// - catalog/environment verdicts (`catalog-error`, `manifest-error`, +/// `unknown-enum-member`) — fixed-vocabulary questions the client's +/// project check answers the same way. +const DOCUMENT_SYNTAX_CODES: [&str; 16] = [ "lex-error", "parse-error", "workshop-source", @@ -60,6 +78,8 @@ const DOCUMENT_SYNTAX_CODES: [&str; 14] = [ "settings-placement", "macro-invalid", "do-while-placement", + "invalid-string-entity", + "unknown-string-entity", ]; #[derive(Debug, Deserialize)] diff --git a/crates/opy-provider/tests/lpp.rs b/crates/opy-provider/tests/lpp.rs index 7b1abe33..40191402 100644 --- a/crates/opy-provider/tests/lpp.rs +++ b/crates/opy-provider/tests/lpp.rs @@ -2588,6 +2588,41 @@ fn validate_edits_applies_normative_rules() { "failingEditIndex": 1, }) ); + + // String entities are fixed vocabulary: a malformed `\&entity;` escape in + // the document's own text is a `syntaxError`, not a deferred verdict. + let entity = |session: &mut Session, id: i64, new_text: &str| { + session.request(json!({ + "jsonrpc": "2.0", + "id": id, + "method": "lpp/validateEdits", + "params": { + "document": { + "uri": "file:///project/entity.opy", + "languageId": "opy", + "version": 3, + "text": "rule \"r\":\n @Event global\n wait(0.1)\n", + }, + "edits": [{ + "range": { + "start": { "line": 0, "character": 6 }, + "end": { "line": 0, "character": 7 }, + }, + "newText": new_text, + }], + }, + })) + }; + assert_eq!( + entity(&mut session, 10, "r \\&bogus;")["result"], + json!({ "valid": false, "version": 3, "reason": "syntaxError" }), + "an unknown entity in the edited document is a syntaxError" + ); + assert_eq!( + entity(&mut session, 11, "r \\&bogus")["result"]["reason"], + "syntaxError", + "an unterminated entity in the edited document is a syntaxError" + ); session.shutdown(); }