Skip to content

Fix issues from a full project review - #1

Merged
ghosthack merged 10 commits into
mainfrom
claude/clever-ptolemy-key2ws
Nov 7, 2026
Merged

ghosthack merged 10 commits into
mainfrom
claude/clever-ptolemy-key2ws

Conversation

@ghosthack

@ghosthack ghosthack commented Nov 7, 2026 •

Copy link
Copy Markdown
Owner

Fixes from a full review of docview 0.2.0. Each code fix has a regression test that fails without it; ./mvnw verify passes.

Safety and correctness

  • Wide HTML tables could exhaust memory. Every row was padded to the widest row with no column cap, so a ~1 MB table ran out of a 2 GB heap. HTML tables are now capped at 128 columns (the Markdown limit), and cells past the cap merge into the last column without losing text.
  • One caller could break a shared cache build. BackgroundCache gave the same in-flight future to every request/prefetch caller, so one cancel() or orTimeout() broke all of them. Each caller now gets its own future.
  • Highlight budgets counted the wrong time. The budgets included grammar compilation and waiting on other documents, and one TextMate call could run about 1 s past the per-document budget.
    • HighlightSession gains a default tokensForLine(line, maxNanos, spent). The budget passes its remaining time, and TextMate reports regex search time only.
    • LineDeadline no longer double-counts a finished search, and it keeps thread interrupts that Joni cleared.

Views

  • Scroll memory saved the wrong position. Showing two documents before a layout pass saved the outgoing document with the previous document's row. Both views now remember the row they scrolled to until the next layout.
  • Scroll keys swallowed app shortcuts. Ctrl/Alt/Meta/Cmd + arrows, J/K, Home/End and Page keys now reach the application. The same goes for Ctrl+Space in XmlTreeView.

Markdown and HTML display

  • Numbered lists: WebView ordered lists honour their first number (start=).
  • Horizontal rules: spaced rules (* * *, - - -, _ _ _) are now recognized.
  • Contents tree: heading text now comes from the renderer, so it matches the page.
  • <object>/<applet>: the tags are dropped and their fallback content is shown, so an unclosed one no longer hides the rest of the document.
    • Behaviour change: content inside a closed <object> now appears too. The README's Safety section says so.

Release, CI and licensing

  • Release workflow: jobs check out github.sha instead of the tag name, validate fails if the tag has moved, and the dry run is limited to main. The dry run now also checks that every bundle artifact is signed, and the help/versions plugins are pinned.
  • CI: the macOS job moves from macos-14 to macos-15. On macos-14 the job was cancelled before any runner was assigned, twice.
  • Licence notices: the textmate jar now ships the MIT notices for each bundled grammar upstream (grammars/THIRD-PARTY-NOTICES.txt) and for the code TM4E was translated from (META-INF/licenses/tm4e/NOTICE-MIT.txt).

Known limits

  • Cold-start highlighting: on a grammar's first line, regex search runs slowly before the JIT warms up, and that time still counts toward the budget.
  • Minor Markdown gaps not fixed here: backslash escapes, Total | + --- parsing as a table, mis-nested **a _b** c_, and CDATA script text shown escaped.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS

claude added 10 commits November 7, 2026 04:51
HTML tables padded every row to the widest row with no column cap, so a
~1 MB table of one wide row over many short ones grew as rows x columns
and ran out of memory. Cells past the 128-column cap (the Markdown limit)
now join the last column, keeping their text.

BackgroundCache handed the same in-flight future to every request and
prefetch caller, so one caller cancelling or timing it out broke every
other caller and later requests until the build finished. Each caller
now gets its own follower future.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
BlockView and XmlTreeView saved a document's scroll position from the
flow's first visible cell, which only moves on a layout pass. Showing
two documents before a pulse (fast keyboard browsing) saved the
outgoing document with the previous document's row, so it later
reopened scrolled to an unrelated position. Each view now keeps the row
it last scrolled to until its next layout pass and reports that row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
The reader's key filter matched UP, DOWN, J, K, HOME, END and the page
keys without looking at modifiers and consumed them, so application
accelerators such as Ctrl+K, Ctrl+J, Cmd+Up or Ctrl+End scrolled the
reader instead of firing while it had focus. Keys with Ctrl, Alt, Meta
or the shortcut modifier now pass through; Shift still pages back with
Space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
Ctrl+Space and similar accelerators toggled the focused branch and were
consumed while the tree had focus; only a plain Space toggles now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
Every release job checked out the tag by name, so a tag force-moved
while the workflow ran could publish code that validate and CI never
saw. All three checkouts now use the event's commit, and validate fails
if the tag no longer points at it.

The deploy and the manual bundle dry run, which both sign with the
release key, now run in a maven-central environment that can carry
required reviewers and the secrets, and the dry run is limited to main.
The dry run also checks that every artifact in the bundle is signed
instead of only that a bundle exists, and the help and versions
plugins it invokes by prefix are pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
The docview-textmate jar carried only the VS Code MIT license and the
TextMate Bundle License, but most grammars come from other MIT
upstreams (MagicStack, Shopify, GitHub/Atom, Jeff Hykin, Microsoft
grammar repos, the Atom Java grammar via redhat-developer), and MIT
requires each one's copyright and permission notice to travel with the
copies. The vendored TM4E core likewise translates MIT code from
vscode-textmate, vscode and node-oniguruma, whose notices were only in
source headers that compiled classes drop.

Add grammars/THIRD-PARTY-NOTICES.txt with each upstream's notice at the
pinned commit (VS Code's fuller cgmanifest notice where it records
one), and tm4e/NOTICE-MIT.txt packaged under META-INF/licenses/tm4e/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
The project has a single maintainer, so an approval gate and
environment-scoped secrets add setup without guarding against anyone.
The release stays pinned to the published commit, and the dry run stays
limited to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
The per-document budget timed whole tokensForLine calls, and the
TextMate session budget timed whole tokenizeLine calls. Both counted
grammar rule compilation and time spent waiting for the shared grammar
lock while another document (or a low-priority prefetch) tokenized, so
concurrent builds drained a document's budget. The document budget was
also only checked before each call, while one TextMate call could
tokenize every earlier line of a block and run up to the 1 s session
budget past it.

HighlightSession gains a default tokensForLine(line, maxNanos, spent):
the budget passes its remaining time and is charged what the session
reports. The default keeps the old behaviour (wall time, no allowance),
and the guarded wrapper forwards it and charges wall time to a session
that fails before reporting. TextMate caps each line's deadline at the
smaller of 100 ms, the session's remaining budget and the call's
allowance, and reports only regex search time.

LineDeadline also no longer counts a just-finished search twice when
the watchdog races its bookkeeping, and keeps a thread interrupt that
Joni turned into an InterruptedException and cleared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
- Ordered lists in WebView pages now carry a start attribute from their
  first marker, so "3. a" shows 3 and an ordered run after a bullet run
  continues its numbering, as the native reader already showed.
- Thematic breaks may have spaces between markers ("* * *", "- - -",
  "_ _ _"), per CommonMark; they rendered as an emphasized list item or
  paragraph. One shared check now replaces five copies of the pattern,
  and mixed markers ("- * -") are no longer a rule.
- The contents tree takes heading text from the renderer's own inline
  output instead of separate patterns, so "snake_case_name" and a code
  span holding link syntax read the same as on the page.
- An object or applet element no longer hides everything after it when
  left unclosed: its tags are dropped and its fallback content is shown
  through the same whitelist, as a browser without plugins does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
The macos-14 job was cancelled before a runner was assigned on two
attempts, with no steps run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwoQ7jw5qzPPePBj6NEpaS
@ghosthack
ghosthack merged commit 11a164b into main Nov 7, 2026
3 checks passed
@ghosthack ghosthack mentioned this pull request Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants