Repository navigation
Read named state sets and states written as values of their sets - #17
Conversation
The verifier now accepts @stateset(name = "phase", value = {...}) and a state written as a value of its set, phase(this) == open, which it reads as open(this), and phase(this) != open as !open(this). The parser took such a from for a condition on every state, so the diagram lost its transitions. A comparison of a one-argument function with a state, in either order, is now that state, and != negates it, wherever the shorthand is read: from, to, constructors and factories. Conditions keep the form the spec wrote. Each state machine carries stateSetName, the name given to its state set, or null when it has none, so a client can label the diagram with the name the verifier's messages use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 1
ResponseBoth declined:
No changes in this round. |
open(this) != false is not phase(this) != open: it means open(this), so its complement drew the transition from the other states. Only a comparison that is itself a state value is read as its negation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 2
ResponseFixed in b5a1061: |
Codex review, round 3
|
Clean-code reviewAudit of this PR's diff against the clean-code-principles checklist (
No changes. |
Brings [liquidjava-fsm#17](liquid-java/liquidjava-fsm#17) to the extension, for the verifier's named state sets and states written as values of their sets (liquid-java/liquidjava#498, #510, #511; design in liquid-java/liquidjava#502). **Depends on a liquidjava-fsm 0.2.3 release** of liquidjava-fsm#17: until it is on Maven Central, `server` does not build in CI. ## What changes - `server/pom.xml`: liquidjava-fsm 0.2.2 → 0.2.3, which reads `phase(this) == open`, `open == phase(this)` and `phase(this) != open` in `from`/`to` as the states they name, and reports each diagram's `stateSetName`. - `StateMachineDTO`: passes `stateSetName` to the client (the `liquidjava/fsm` request returns the fsm records, so it gets it as is). - State Machine view: a named state set's diagram is titled with its name, also when it is the class's only state set, so it can be matched with the error message (`doorState(d) == closed`). A state set without a name keeps its states as the title, as now. ## Checked, no change needed The verifier sends the same shapes to the IDE as before (`git diff fbc5e9f6 origin/main`): `StateErrorContext` still names states by their names, and hints are unchanged. Only the text of a state error's message, `expected` and `found` changes, to `doorState(d) == closed` for named sets. The client renders those strings as they come, with the generic highlighter, and nothing in the client parses state names out of message text: the diagram marks and the summary line use `errorContext`. The verification view shows the new form as is (last screenshot). ## Screenshots Headless VS Code with this branch, liquidjava-fsm#17 installed locally as 0.2.3 and the verifier built from liquid-java/liquidjava main, on this spec (`d.lock(); d.open();` as the error): ```java @stateset(name = "doorState", value = {"closed", "opened"}) @stateset(name = "lockState", value = {"unlocked", "locked"}) public class Door { @StateRefinement(to = "doorState(this) == closed && lockState(this) == unlocked") public Door() {} @StateRefinement(from = "doorState(this) == closed && lockState(this) == unlocked", to = "doorState(this) == opened") public void open() {} @StateRefinement(from = "doorState(this) == opened", to = "doorState(this) == closed") public void close() {} @StateRefinement(from = "doorState(this) == closed && lockState(this) != locked", to = "lockState(this) == locked") public void lock() {} @StateRefinement(from = "lockState(this) == locked", to = "lockState(this) == unlocked") public void unlock() {} } ``` Before (main, fsm 0.2.2), the diagrams are wrong: `open` and `lock` go from every state with the whole `from` as a condition, `close` and `unlock` are missing, the summary only names `lockState`, and the titles are the states. After, the diagrams match the spec, the conditions keep the form the spec wrote, and the titles are the set names: | | Before | After | |---|---|---| | Show all | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/178/before-all.png" width="380"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/178/after-all.png" width="380"> | | Focus on error | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/178/before-focus.png" width="380"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/178/after-focus.png" width="380"> | The verifier's message for the same error (no change here; shown for context): <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/178/verification.png" width="380"> ## Not in this PR - The verifier bump: main of liquid-java/liquidjava is not released yet. When it is, `DiagnosticConverterTest` needs the new `StateRefinementError` constructor argument (`display`, from #511) and the `RefinementError` one (`aliases`); the server code itself compiles against it. - The walkthrough and README examples stay in the shorthand until the extension ships a verifier that accepts named sets. ## Tests - client: `vitest run` 124 passed (one new test, fails without the change), typecheck and lint clean - server: `mvn test` 35 passed, against liquidjava-fsm#17 installed as 0.2.3 in an isolated local Maven repository 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Follows the verifier's named state sets (liquid-java/liquidjava#498) and states written as values of their sets (liquid-java/liquidjava#510, design in liquid-java/liquidjava#502).
Problem
With the verifier on main, a spec can write
The parser took
open == phase(this)for a condition that holds in any state, soclosewas drawn fromopen,closedandbroken, each with the conditionopen == phase(this).phase(this) != openwent the same way, and a value of another set in atowas kept instead of being left out.Change
getStateNamealso readsset(x) == stateandstate == set(x)(a one-argument function compared with a state) as the state, andgetNegatedreadsa != bas the negation ofa == b, like!. This is the verifier's reading (StateValues.toShorthand), and it applies wherever the shorthand is read:from,to, constructors and factories,this.phase(),phase(),old(this), and generated names such asstate2(this) == unlocked.phase(this) != broken.StateMachinegetsstateSetName: thenameof its@StateSet, or null when it has none. Generated names (state1, ...) are not exposed, since the verifier does not show them in messages either. The old six-argument constructor is kept, as vscode-liquidjava uses it.Unchanged: the parser stays as lax as for the shorthand (it does not check that the function is a state set of the class, nor that a parameter is named like a state); a spec where that matters is rejected by the verifier.
Tests
One fixture,
StateValues.java(a named and an unnamed set,==in both orders,!=,this.phase(),phase(), a generated name, mixed with the shorthand), and one test that fails without the change.mvn testpasses locally (33 tests).Screenshots of the diagrams before and after, in VS Code: liquid-java/vscode-liquidjava#178.
A release is needed before vscode-liquidjava can use
stateSetName(PR to follow there).🤖 Generated with Claude Code