GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Show variables by their name, with a line only to tell instances apart by CatarinaGamboa · Pull Request #493 · liquid-java/liquidjava

Conversation

@CatarinaGamboa

@CatarinaGamboa CatarinaGamboa commented Nov 5, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Closes #492

Variables in errors no longer carry a superscript counter (box¹⁵). That counter matched nothing in the code, and it mixed Latin-1 ¹²³ with the Superscripts block, which many fonts draw unevenly. Each error now names its variables within one scope:

  • A variable that no other variable in the scope is written like keeps its plain name.
  • When several are written alike, one keeps the plain name: the declared variable, or else the latest instance the message shows. Each other one gets the line its value comes from, x@12.
  • Two tagged variables from the same line (or with no line) are numbered x@12#1, x@12#2. Two different instances never print the same.

The message, counterexample and Found steps of an error share the same names. The context view follows the same rule within each refinement. Verification, internal names, counters and the translation table are unchanged; this is presentation only. toSuperscript is removed.

Before / after

Plain names (A2_10 RandomFailureSupplier, the case reported in the issue):

before: Refinement Error: failures.size()⁸⁴² == size(this.failures) && failures.size()⁸⁴² >= 0 is not a subtype of failures.size()⁸⁴² > 0
         --> Counterexample: failures.size()⁸⁴² == 0
after:  Refinement Error: failures.size() == size(this.failures) && failures.size() >= 0 is not a subtype of failures.size() > 0
         --> Counterexample: failures.size() == 0

A3_07 ScannerOutputStage (the only study error with two instances of a name in the message):

before: found startTiling(preview¹¹¹) && state2(preview¹¹¹) == state2(preview¹⁰⁹) && compressionSet(preview¹¹⁶) && state1(preview¹¹⁶) == state1(preview¹¹¹) but expected tilingExplicit(preview¹¹⁶) || tilingSet(preview¹¹⁶)
after:  found startTiling(preview@51) && state2(preview@51) == state2(preview@50) && compressionSet(preview) && state1(preview) == state1(preview@51) but expected tilingExplicit(preview) || tilingSet(preview)

A1_56 EditCommand: the message, and the original Found the panel steps back to:

before: expected redoable(undoManager⁵⁵⁷) but could not prove it
after:  expected redoable(undoManager) but could not prove it

before                                              after
∀#getUndoManager_result⁵⁵⁵ true                     ∀#getUndoManager_result true
∀#operation_result⁵³⁷ true                          ∀#operation_result@41 true
∀#if_condition⁵³⁸ !#operation_result⁵³⁷             ∀#if_condition@41 !#operation_result@41
∀#operation_result⁵⁵⁰ true                          ∀#operation_result@56 true
∀#operation_result⁵⁵¹ true                          ∀#operation_result true
∀#if_condition⁵⁵² !(#operation_result⁵⁵⁰ || …⁵⁵¹)   ∀#if_condition !(#operation_result@56 || #operation_result)
∀undoManager⁵⁵⁷ undoManager⁵⁵⁷ == …⁵⁵⁵               ∀undoManager undoManager == #getUndoManager_result

Across the test suite, 11 of 437 errors still show a tag in the message. All of them are before-and-after values (s--, b = -b, a typestate object across calls). Counters remain only where values on one line must be told apart, e.g. n@11#1 in for (; n > 0; n -= 2);.

API for the extension (liquid-java/vscode-liquidjava#175)

  • LJError.getDisplayNames() returns the error's DisplayNames for a RefinementError or StateRefinementError (null for other errors).
    • Binder names, counterexample variables and translation-table keys: names.get(internalName).
    • Expected and the predicate of each step: ExpressionFormatter.format(expression, names).
  • Context view: ExpressionFormatter.format(refinement.getExpression(), ContextHistory.getInstance()::lineOf) for VariableDTO.refinement / mainRefinement.
  • RefinementError.getFoundAliases() / getExpectedAliases() (Show the definitions of the aliases a refinement error uses #489) use the same names: invocation and expansion are formatted with the error's DisplayNames, so an invocation reads as in Found/Expected, e.g. Below(b@9, 10) with expansion b@9 < 10.
  • VariableFormatter.format and ExpressionFormatter.format(expression) keep working without a scope. They write plain names and number clashes within the expression.
  • The webview tokenizers should treat name@12#2 as one identifier and can render the tag as <sub>.

Tests

  • DisplayNamesTest: line tags with the latest shown value plain; numbering for a shared line or no line, and the declared variable kept plain.
  • TestFoundBinders (new Basket.negate fixture): message, counterexample and original Found rows name b@85, b@86 and b alike.
  • ContextHistoryTest: the refinement of b after b = -b reads b == b@19 && b > 0.
  • Existing assertions on superscripts are updated to plain names where they compare them; assertions that strip superscripts are left as they are.

mvn test (rebased on main after #489): 712 tests, 0 failures. Diff vs main: +353/−113 in 23 files.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@CatarinaGamboa

CatarinaGamboa commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Codex review, round 1

[P2] Resolve context variable lines within the relevant scope (ContextHistory.java): when a launch verifies several methods or files that use the same variable name, lineOf searches every saved local and global variable by name and takes the first match from unordered sets, so format can label an earlier value with a line from a different method or file.

Not relevant, so no change:

  • Instance names are unique within a launch. A line is only looked up to tag an instance. Instance names carry the context counter, which is reset once per launch (RefinementProcessor → reinitializeAllContext), and ContextHistory is cleared at every launch. So each #x_N matches exactly one saved variable.
  • Declared names are never tagged. A declared name such as b, which can repeat across methods, always keeps the plain name when it appears together with its instances.

Nothing else relevant, so the loop ends here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@CatarinaGamboa
CatarinaGamboa force-pushed the fix/instance-display-names branch 2 times, most recently from c732fd3 to 275ca17 Compare November 5, 2026 19:37
@CatarinaGamboa

CatarinaGamboa commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Codex review, round 2 (diff rebased on main after #489, with alias expansions named by the error's DisplayNames)

[P2] Resolve context variable lines within the owning class (ContextHistory.java): when an LSP launch processes multiple classes, ContextHistory keeps variables from all of them, while Context.reinitializeAllContext() resets instance counters for each class. Two classes can both have a #x_0 at different lines, and the name-only lookup can pick either.

Not relevant, so no change. This is round 1's finding again, and the premise does not hold: reinitializeAllContext() has one caller, RefinementProcessor.process, which Spoon runs only on the root package (isToBeProcessed returns pkg.isUnnamedPackage()). Packages and classes are then walked from there, so the counter is reset once per launch, not per class. ContextHistory is cleared at the start of every launch. Each #x_N therefore matches one saved variable.

Nothing else relevant, so the loop ends here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@CatarinaGamboa @claude
Show variables by their name, with a line only to tell instances apart
6a3baee
Errors and context refinements wrote every variable instance with its
verifier-wide counter in superscript digits (box¹⁵), which matched nothing
in the code and mixed two Unicode blocks that many fonts draw unevenly.
Each error, and each context refinement, now names its variables in one
scope: a variable written alike to no other keeps its plain name; when
several are, the declared variable or the latest instance shown keeps it
and each other one gets the line its value comes from (x@12), numbered
only when they share a line or have none (x@12#1). The message,
counterexample and Found steps share the error's names, exposed with
getDisplayNames(); ContextHistory.format(refinement) does the same for the
context view. Verification, internal names and the translation table are
unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@CatarinaGamboa
CatarinaGamboa force-pushed the fix/instance-display-names branch from 275ca17 to 6a3baee Compare November 5, 2026 20:45
@CatarinaGamboa

CatarinaGamboa commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Codex review, round 3 (after the clean-code pass: shared ErrorMessage, one getDisplayNames() on LJError, VCSimplificationResult.getOriginal(), context formatting through ExpressionFormatter)

[P2] Skip unknown lines before selecting a placement (ContextHistory.java): when a saved variable has a placement with an invalid Spoon position, PlacementInCode.getLine() returns null, and findFirst() throws a NullPointerException instead of lineOf() returning unknown. Filter out null lines after the mapping.

Fixed. lineOf now filters null lines after mapping placements to lines, so an invalid position counts as unknown and no longer throws (Stream.findFirst throws on a null element).

Nothing else relevant, so the loop ends here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@CatarinaGamboa
CatarinaGamboa merged commit ae278d2 into main Nov 6, 2026
1 check passed

Uh oh!

There was an error while loading. Please reload this page.

@CatarinaGamboa CatarinaGamboa mentioned this pull request Nov 11, 2026
Render the verifier's line tags as subscripts and name every part of an error alike liquid-java/vscode-liquidjava#196
Merged
CatarinaGamboa added a commit to liquid-java/vscode-liquidjava that referenced this pull request Nov 11, 2026
@CatarinaGamboa @claude
Render the verifier's line tags as subscripts and name every part of …
1bd7f42
…an error alike (#196)

Closes #175

Verifier 0.0.38 (liquid-java/liquidjava#493, already the dependency on
main) names variables as in code and, only when an error has several
values of one name, tags the others with the line their value comes
from: `preview@51`. The message and the Context tab already used those
names. The rest of the panel did not: the server formatted the Expected,
the Found steps, the counterexample and the translation table with the
verifier's default formatter. That formatter numbers clashes within each
expression, so the Found of A3_07 showed three different values of
`preview` all as `∀preview`, and `preview#1` meant a different value on
each line.

## What 0.0.38 sends
- **Tag format:** `x@51`. When tagged values share a line, `x@25#1`,
`x@25#2`; with no line, `x#2`. A call argument can carry one too:
`failures.size()@84`. No Unicode superscripts any more.
- **Where:** the message, the counterexample in the message, the message
notes and hints, and the Context tab (`ContextDisplay`, merged in #186)
already use the names. `LJError.getDisplayNames()` gives the error's
names for refinement and state refinement errors; nothing else is
pre-formatted for the IDE.

## What changes
**Server** (`server/src/main/java/dtos`)
- `RefinementErrorDTO` and `StateRefinementErrorDTO` format the
expected, each step's binder and predicate (`VCImplicationDTO`), and the
counterexample with `error.getDisplayNames()`.
- `TranslationTableDTO` keys each entry by the error's name for it
(`preview@51`), so a tagged binder still opens its source. Errors
without names keep the plain key.

**Webview**
- A tag is drawn as a subscript: `preview` with a small `l51` below,
titled "value of preview at line 51" (`x@25#2` → `l25#2`, "value 2 of x
at line 25"). This applies in every highlighted expression (Expected,
Found, steps, counterexample, Context), in the binders, and in the code
of messages, notes and hints. Copy, the hover and the Problems panel
keep the ASCII `preview@51`.
- The step diff keeps `preview@51` as one token, so a changed tag
highlights its whole variable.
- String and character literals are kept whole when highlighting and
diffing, so `"a@1"` is not read as a tag.
- The regexes no longer match `⁰-⁹`.
- The Context tab's Name column renders a variable without a source
position the same way as one with a position (before, it was raw text).
- Rows the error mentions are still marked in the Context tab: the
translation-table keys are compared without their tag, as before.

## Against the issue's to-do
- Render the tag: done (subscript + tooltip). **Click-to-source:** works
on binders (`∀preview@51`) through the re-keyed translation table. A tag
inside a predicate shows only the tooltip, because the highlighter has
no translation table. The line number is in the tag itself.
- Tokenize as one identifier: done (step diff and highlighter). The
Unicode superscript handling is gone.
- **Context view: already done** by #186 (`ContextDisplay` with
`DisplayNames` and `ContextHistory.lineOf`). This PR only renders its
tags the same way.
- Translation table keyed by display name: done.
- **Bump the verifier: already done**, main depends on 0.0.38.

## Screenshots (headless VS Code, light theme, verifier 0.0.38)
**A3_07 ScannerOutputStage**, the study exercise with several values of
`preview`.

| | before (main) | after |
|---|---|---|
| Found, a step back (9/10) |
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-before-step.png)
|
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-after-step.png)
|
| Found, original (1/10) |
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-before-original.png)
|
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-after-original.png)
|
| Context tab |
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-before-context.png)
|
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A3_07-after-context.png)
|

**A2_08 LogWindow**, plain names (no change, as for every other study
exercise):

| before (main) | after |
|---|---|
|
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A2_08-before.png)
|
![](https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/175/A2_08-after.png)
|

## Not in this PR
- Explaining generated names such as `#if_condition` (#195).
- **Different scopes.** The Context tab names variables per context
(declared variable plain) and the error per error (latest shown value
plain). The same value can be `preview@54` in the Context tab and
`preview` in the error, as in the A3_07 screenshots. Making them agree
would mean a verifier change.

## Expected conflicts
- #185 (`study-found-box`): merges cleanly with its current head. It
changes `vc-implications.ts` and `styles.ts`; this PR adds one rule to
`styles.ts` and changes `TOKEN_PATTERN` and `renderBinder` in
`vc-changes.ts`. Any conflict would be in `styles.ts` (adjacent rules).
- #193 is merged; this PR changes one line of `message-text.ts`.

## Tests
- Server `DiagnosticConverterTest`: verifies a `b = -b` fixture and
checks that the message, every Found step (`∀b@6 b@6 == 8`, `∀b@7 b@7 ==
b@6 && b@7 > 0`, `∀b b == -b@7`), the expected, the counterexample and
the translation-table keys use the same names. On main the steps read
`∀b b == b#1 && b > 0`.
- Client `vc-changes.spec`: tags render as subscripts with their tooltip
(also after a call and with a number). A changed tag is diffed with its
variable. A literal is left alone. `diagnostics.spec`: a tag in a
message's code, next to a literal. `context.spec` was updated to the
rendered tag.
- Local: `npm run test:unit` (134), `test:typecheck`, `lint`, `mvn -B
package` (44), `VSCODE_TEST_VERSION=minimum xvfb-run -a npm test` (9 + 1
passing).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Variable superscripts match nothing in the code and render as separate digits

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@CatarinaGamboa
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.