Skip to content

Modernize eXide: CM6 editor, REx parser, Roaster API, LSP integration#794

Open
joewiz wants to merge 22 commits into
eXist-db:developfrom
joewiz:pr/modernize-eXide-clean
Open

Modernize eXide: CM6 editor, REx parser, Roaster API, LSP integration#794
joewiz wants to merge 22 commits into
eXist-db:developfrom
joewiz:pr/modernize-eXide-clean

Conversation

@joewiz
Copy link
Copy Markdown
Member

@joewiz joewiz commented May 11, 2026

Summary

Modernizes the eXide web application, replacing core dependencies and adding language-service-powered intelligence, real-time monitoring, and a REST API. This PR contains only the web-app modernization — the eXide Desktop (Tauri) work that was part of #778 is deferred to a follow-on PR per @line-o's request.

Reviewers familiar with eXide pre-modernization and the goals discussed in #778 should find this PR much easier to follow: 19 topical commits instead of 56, no desktop scope, end-state dependencies (existdb-openapi, roaster) assumed final.

Highlights

  • REx parser generated from W3C XQuery 3.1 EBNF replaces xqlint; visitor infrastructure unchanged.
  • CodeMirror 6 replaces Ace, with new CM6 autocomplete, semantic highlighting, and language helpers for XQuery, XML, HTML, CSS, Less, JS, JSON, Markdown.
  • Prettier integration (XQuery, XML, HTML, CSS, Less, JS, JSON, Markdown).
  • jQuery, jQuery UI, AG Grid removed — vanilla JS + native <dialog> via dialog-utils.js.
  • OpenAPI/Roaster REST API replaces legacy XQuery modules.
  • Redesigned UI: toolbar, status bar, dialogs, tabs, dark mode, outline.
  • Cypress integration tests (autocomplete, monitor, dialogs, file save, go-to-definition, references, tag matching, scope, selection match, query execution, pagination, large result, serialization, WebSocket).
  • WebSocket transport for real-time diagnostics + monitoring push (running query / recent queries, kill query). Diagnostics payloads follow LSP shape (e.g. textDocument/publishDiagnostics); see also existdb-openapi for the underlying language-service endpoints.
  • Server-side cursor pagination via cursor:eval / cursor:fetch, with AbortController cancel, update-query warning, and adaptive serialization.

Dependencies

  • eXist-db develop (eXist 7.0+) with Upgrade Jetty 11→12 (EE10, Jakarta Servlet 6.0) and add WebSocket module with streaming query evaluation exist#6145 (Jetty 12 / WebSocket support, merged). CI runs against existdb/existdb:latest; the eXist-db release-version matrix entry is dropped because eXide 4.0 requires the 7.0 lang:* / cursor:* modules from existdb-openapi, which are only available against develop.
  • existdb-openapi (formerly exist-api) — provides the lang:* (language services: diagnostics, completions, hover, definition, references, symbols) and cursor:* (server-side cursor: eval, fetch, close) functions consumed by eXide. Replaces the standalone exist-lsp package. CI downloads the release XAR like roaster.
  • roaster ≥ 1.8.0 — Roaster REST API framework.

Test plan

  • npm test — Node unit tests (179 tests) ✅ verified in latest CI run (# tests 179 # pass 179 # fail 0)
  • npm run build produces build/eXide-4.0.0.xar ✅ verified in latest CI run (Creating xar eXide-4.0.0.xar)
  • Install into eXist + existdb-openapi + roaster; eXide loads at /exist/apps/eXide/
  • npx cypress run — full E2E suite
  • Smoke test in browser: new file, save, run query, paginate large result, cancel, WebSocket monitoring, language-service diagnostics, go-to-definition, find references, format, dark mode, tag matching, outline

Note: This PR's call sites originally used the lsp: XQuery namespace and /api/lsp/* URL prefix (when the upstream package was exist-api). Both were renamed in existdb-openapi#18: lsp:lang: for language-service functions, plus a new cursor: namespace for eval/fetch/close. The CSS class cm-lsp-hover and helper file src/lsp-hover.js were renamed to cm-lang-hover / src/lang-hover.js to match.

Supersedes #778.

joewiz and others added 19 commits May 11, 2026 11:32
Replace eXide's bundled xqlint parser with a REx-generated parser built
from the W3C XQuery 3.1 + Update 3.0 + Full Text 1.0 EBNF grammar.
Includes adapter layer, static analysis, and comprehensive tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migrate from Ace to CM6 with full language support, syntax highlighting,
autocompletion, and keybinding infrastructure. Includes editor-utils
shim layer and CM6 bundling script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bundle Prettier with plugins via esbuild. Replaces xqlint CodeFormatter
with multi-language formatting support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace jQuery AJAX with fetch, jQuery UI dialogs with native <dialog>
via dialog-utils.js, and AG Grid with vanilla HTML tables. Remove all
jQuery/AG Grid CSS themes and scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 15+ legacy XQuery modules with a Roaster-based OpenAPI REST API.
Endpoints for storage, query execution, packages, authentication,
templates, search, admin, sync, and editor operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive UI overhaul including redesigned toolbar, status bar with
mode selector, native dialog system, tab overflow controls, filterable
outline panel, dark mode with system theme detection, keyboard shortcuts
dialog, and responsive layout improvements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CM6 autocompletion with LSP-aware function completions, semantic
highlighting via CM6 decorations, hover tooltips, function documentation,
and language helpers for JSON, Less, Markdown, JavaScript, CSS, and XML.
Remove dead Ace-era code (ace-modes.js, popup.js, splitter.js).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cypress E2E tests for autocomplete, diagnostics, outline, query execution,
preferences, formatting, and more. Remaining frontend updates: layout,
find/replace, menus, directory browser, drag-and-drop, history, and
monitoring panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Editor features:
- Matching tag highlight for XML/HTML (open/close tag partner)
- XML/HTML scope breadcrumb in status bar (XPath at cursor)
- XML Rename Element with multi-cursor (both tags updated)
- XPath signatures with positional predicates in outline
- Navigation flash on all jump-to actions (gotoLine, gotoSymbol,
  gotoDefinition, outline click, rename element)
- Selection match highlighting, Cmd+D multi-select
- Diagnostics panel (Cmd+Shift+D), autocomplete-on-type preference
- Dynamic namespace registry from server, local hover fallback
- Deferred splash screen, bracket matching restyled blue
- Dialog focus on primary action button instead of close ×
- Enable multiple selections for multi-cursor operations
- Unprefixed default namespace completion (conc → fn:concat)
- Preserve namespace prefix on completion acceptance
- Fix duplicate outline entries for self-closing XML elements
- Fix JS gotoSymbol when outline not yet populated

exist-next compatibility:
- controller.xq, auth.xqm: xs:dayTimeDuration cast
- admin.xqm: dynamic file module detection via util:eval
- sync.xqm: dynamic file:sync() via util:eval
- packages.xqm: fix collection-to-package resolution
- query.xqm: make lsp:* import optional
- compileError: parse line/column from message when lsp returns line 0

CI:
- Skip autocomplete/outline tests when lsp:* unavailable
- Move LSP-dependent Cypress tests to lsp/ subdirectory
- Add 4 Cypress tests for prefix/unprefixed completion scenarios

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/query/references with {query, line, column, base} returns
all references to the symbol at position. Uses dynamic function-lookup
for lsp:references (gracefully returns empty when unavailable).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Client-side WebSocket transport (src/ws-transport.js) providing:
- Persistent bidirectional connection to eXist-db
- JSON-RPC message framing (compatible with LSP protocol)
- Request/response with callbacks for hover, completions, etc.
- Server-push notifications for diagnostics, monitoring events
- Auto-reconnect with exponential backoff
- Auto-connect deriving WebSocket URL from page location
- Graceful fallback when WebSocket unavailable

API: eXide.ws.connect(), .send(), .notify(), .on(), .off()

Designed for use with eXist-db's WebSocket support (PR #6145).
Phase 1 target: real-time monitoring (replace HTTP polling).
Phase 2: LSP over WebSocket (push diagnostics, incremental edits).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ncoding

Autocomplete was doubling namespace prefixes (e.g. repo:repo:deploy) when
the user typed a prefixed name like "repo:" — the nsPart logic now checks
whether the server result already includes the prefix before prepending it.

Storage API calls in eXide.js and directory.js used encodeURIComponent on
full paths, encoding slashes as %2F and causing 400 errors. Fixed by using
the existing apiPath() helper that encodes each segment individually.

Also replaced fixed cy.wait() sleeps in monitor and websocket specs with
assertion-based retries, cutting suite time by ~2 minutes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…omplete_spec

cy.wait(300/200/500) was used as a proxy for "completions API response arrived".
Now each test that accepts a completion intercepts POST /api/query/completions
and waits on the named alias, making timing deterministic rather than guessed.
Also converts leftover var → const in the filter test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ground

Generated from the x.svg in dashboard/resources/images at 16x16, 32x32,
and 48x48. Added favicon link to src-tauri/connect.html (the only page
template that was missing it).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…al/fetch

Replaces the old XQueryServlet + session.xq flow and the WebSocket streaming
approach with a cursor-based architecture using the new lsp:eval/lsp:fetch/
lsp:close functions from the exist-lsp package.

How it works:
- POST /api/query calls lsp:eval() → stores result sequence in Caffeine cache,
  returns cursor ID + item count + elapsed time
- GET /api/query/{id}/results?start=N&count=M calls lsp:fetch() → serializes
  only the requested page, includes documentURI and nodeId per item
- DELETE /api/query/{id} calls lsp:close() → releases the cursor
- Cursors auto-expire after 5 minutes of inactivity

This restores the key qualities of the old flow:
- Lazy serialization (only viewed page is serialized)
- No client-side buffering (results stay on server)
- Pagination (identical UX with next/previous/first/last)
- Document links (documentURI field for clickable result badges)

Also adds:
- Cancel button in toolbar (visible during execution)
- Timing display bar below results navbar
- WebSocket eval transport (ws-eval.js) for future progress/cancel support
- exist-lsp package dependency in expath-pkg.xml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, "Copy all displayed results to clipboard" concatenated
items without any separator. Now each item is joined with a newline,
making the pasted output readable when the query returns multiple items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Indent and Highlight Matches buttons previously used identical
colors for both hover and active states. Active buttons now show a
solid blue background with white text (dark mode: semi-opaque blue),
making it clear at a glance whether the option is on or off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, clicking an already-open menu header would close it and
immediately re-open it. Now clicking an open menu header closes it,
consistent with standard menubar behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ry library modules

Quick fix system:
- Update quickfix regexes to match new static analysis error formats
  (XPST0081 for namespace prefixes, XPST0008 for undeclared variables)
- Add Quick Fix to XQuery menu and command palette
- Change keybinding from Cmd+Ctrl+Q (conflicts with macOS Lock Screen) to Ctrl+.
- Import action resolves namespace URI from cached module registry instantly
- Filter out java: class references from import location hints
- Add CM6 diagnostic actions so "Import module" appears in error tooltips
- Style diagnostic actions as block-level links below the error message
- Auto-refresh module cache on cache miss; expose app.refreshModuleCache()

Completions for XQuery library modules:
- Supplement lsp:completions() with inspect:inspect-module-uri() for modules
  like kwic, templating that are bundled as .xql files in the uber jar
- Build proper function signatures with XQuery cardinality symbols (?, *, +)
- Filter out stub entries from inspect XML

Hover documentation:
- Enhance hover fallback to fetch full function docs from api/editor/completions
  for prefixed function calls when lsp:hover() returns nothing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joewiz joewiz force-pushed the pr/modernize-eXide-clean branch 3 times, most recently from d05848c to 942f8f2 Compare May 11, 2026 15:42
eXide 4.0 declares a dependency on the exist-api package (which provides
the lsp:* functions). Fetch it from the joewiz/exist-api pre-release in
the same manner as roaster, so the eXist Docker container can deploy
eXide on startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joewiz joewiz force-pushed the pr/modernize-eXide-clean branch from a352168 to 1e427a3 Compare May 11, 2026 15:53
@duncdrum duncdrum moved this to Todo in Action Items May 11, 2026
@duncdrum duncdrum moved this from Todo to In progress in Action Items May 11, 2026
@duncdrum duncdrum added this to v7.0.0 May 11, 2026
@joewiz joewiz force-pushed the pr/modernize-eXide-clean branch 2 times, most recently from 9e2862d to 4e11813 Compare May 17, 2026 02:22
Coordinated update for the breaking rename in
eXist-db/existdb-openapi#18 (formerly eXist-db/exist-api). All four
known consumers (eXide, monex, existdb-langserver, notebook) move
together — no backward-compat aliases.

XQuery namespace switches in modules/api/query.xqm:
- import lsp: -> import lang: (language services) and cursor: (server-side cursor)
- lsp:diagnostics/completions/hover/definition/references/symbols -> lang:*
- lsp:eval/fetch/close -> cursor:*
- function-lookup QName URI updated to .../xquery/cursor

URL/XAR coordinate updates:
- ../exist-api/api/* -> ../existdb-openapi/api/* in src/eXide.js
- CI: download from joewiz/existdb-openapi, pattern existdb-openapi-*.xar
- Note: CI is pinned to v0.9.0-pre.2 — a new pre-release tag will be
  cut from existdb-openapi after PR #18 lands. CI stays red until then.

File renames for naming hygiene:
- src/lsp-hover.js -> src/lang-hover.js
- resources/css/lsp-hover.css -> resources/css/lang-hover.css
- CSS classes cm-lsp-hover* -> cm-lang-hover*
- Updated import in scripts/bundle.js and link in index.html

Comments/docs updated. References to "LSP" as a protocol (e.g.,
"LSP-aware completions", "LSP protocol messages") are preserved since
they describe interop-with-LSP semantics, not the renamed namespace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-clean

# Conflicts:
#	modules/outline.xq
#	src/util.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In review
Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants