Skip to content

Modernize eXide: CM6 editor, REx parser, REST API, LSP integration#778

Open
joewiz wants to merge 55 commits intoeXist-db:developfrom
joewiz:pr/modernize-eXide
Open

Modernize eXide: CM6 editor, REx parser, REST API, LSP integration#778
joewiz wants to merge 55 commits intoeXist-db:developfrom
joewiz:pr/modernize-eXide

Conversation

@joewiz
Copy link
Copy Markdown
Member

@joewiz joewiz commented Mar 15, 2026

Summary

A comprehensive modernization of eXide, replacing legacy dependencies with modern alternatives and adding LSP-powered language intelligence. This consolidates work from multiple prior PRs (#774, #777) into a single reviewable PR with grouped commits.

Commits (grouped for review)

1. Replace xqlint parser with REx-generated XQuery 3.1 parser

REx-generated parser from W3C EBNF grammar (XQuery 3.1 + Update 3.0 + Full Text 1.0). Adapter layer preserves the existing visitor infrastructure. Includes unit tests for parser, visitors, and static analysis.

2. Replace Ace editor with CodeMirror 6

Full migration from Ace to CM6 with language support, syntax highlighting, keybinding infrastructure, and editor-utils compatibility layer.

3. Add Prettier-based code formatting

Multi-language formatting via Prettier + plugins (XQuery, XML, HTML, CSS, Less, Markdown), bundled with esbuild. Replaces xqlint's CodeFormatter.

4. Remove jQuery, jQuery UI, and AG Grid dependencies

All AJAX calls use fetch, dialogs use native <dialog> via dialog-utils.js, DOM manipulation uses vanilla JS. AG Grid replaced with vanilla HTML tables.

5. Add OpenAPI/Roaster REST API and remove legacy XQuery modules

Replace 15+ legacy XQuery modules with a Roaster-based OpenAPI REST API. All backend operations (storage, query, packages, auth, sync, search, admin) handled through typed JSON endpoints.

6. Redesign UI

Toolbar, status bar with mode selector, native dialog system, tab overflow controls, filterable outline panel, dark mode with proper syntax highlighting (One Dark palette via CSS classHighlighter), system theme detection, keyboard shortcuts dialog, monitoring panel, and responsive layout.

7. Add CM6 autocomplete, semantic highlighting, and multi-language helpers

CM6 autocompletion with LSP-aware function completions (prefixed and unprefixed default namespace), semantic highlighting via decorations, hover tooltips with AST fallback, go-to-definition (same-file and cross-module), multi-cursor rename, and language helpers for JSON, Less, Markdown, JavaScript, CSS, and XML. Native HTML tag/attribute, CSS property/value, and JavaScript local variable completions via CM6 language packs.

8. Add Cypress integration tests and remaining frontend updates

E2E tests for autocomplete, diagnostics, outline, query execution, preferences, formatting, and more (172+ tests). Remaining frontend updates: layout, find/replace, menus, directory browser, drag-and-drop, build scripts, login page.

9. Add tag matching, exist-next compat, and CI fixes

  • Matching tag highlight for XML/HTML (cursor on open tag highlights close tag)
  • XML/HTML scope breadcrumb in status bar (XPath at cursor position)
  • XML Rename Element with multi-cursor (both open + close tags)
  • XPath signatures with positional predicates in outline and QuickPicker
  • Navigation flash highlight on all jump-to actions
  • Selection match highlighting, Cmd+D multi-select, diagnostics panel
  • Autocomplete-on-type preference, dynamic namespace registry from server
  • Local hover fallback, unprefixed default namespace completions
  • Deferred splash screen, dialog focus on primary button
  • exist-next compatibility: xs:dayTimeDuration casts, dynamic file module detection, package lookup fix
  • CI: skip LSP-dependent tests when lsp:* module unavailable

10. Add Find All References

  • Shift+F3 / Navigate > Find All References: QuickPicker listing all references to function/variable at cursor
  • Server-side lsp:references() with AST visitor fallback
  • Fix hover error handling for non-200 responses

11. WebSocket transport and monitoring

  • WebSocket transport layer (ws-transport.js) for real-time LSP and monitoring push
  • Monitor panel with WebSocket-based metrics, running/recent queries, kill query button
  • LSP diagnostics push via WebSocket after compilation
  • Auto-connect with exponential backoff reconnect

12. Server-side cursor query execution

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

Before:

User clicks Eval → HTTP POST to XQueryServlet → session.xq stores results
  → 10 GET requests for results/1..10 → pagination via more GETs

After:

User clicks Eval → POST /api/query → lsp:eval() stores result in Caffeine cache
  → GET /api/query/{id}/results?start=1&count=10&method=xml&indent=yes
  → lsp:fetch() serializes just that page with requested serialization params
  → changing serialization mode re-fetches current page (no re-execution)

Key properties:

  • Lazy serialization — only the viewed page is serialized, with configurable W3C serialization parameters
  • No client-side buffering — results stay on the server as live node references
  • Document linksdocumentURI and nodeId available per result item
  • Dynamic serialization — change output method, indent, or highlight-matches without re-executing
  • Pagination — identical UX with next/previous/first/last

This follows the same pattern used by SQL clients (server-side cursors), BaseX GUI (in-process result references), and eXist's own Java admin client (XML-RPC result handles).

Cursor store cache policy

Results are held in a Caffeine cache with configurable eviction:

Setting Default Description
cursor.maximumSize 100 Max concurrent cursors (LRU eviction when exceeded)
cursor.expireAfterAccess 300000 (5 min) Inactivity timeout in ms
cursor.maximumWeight 0 (unlimited) Max estimated total memory in bytes. When > 0, replaces count-based eviction. Weight per cursor = itemCount × 1KB.

Configuration via exist.xml module parameters:

<parameter name="cursor.maximumSize" value="50"/>
<parameter name="cursor.expireAfterAccess" value="600000"/>
<parameter name="cursor.maximumWeight" value="536870912"/>

13. Query cancellation

What it does:

  1. Client-side: AbortController.abort() drops the HTTP connection
  2. Server-side: Kills running queries via system:kill-running-xquery() (admin only)

Risks — cancelling state-modifying queries can leave the database inconsistent:

Expression type Risk Notes
XQUF (insert/delete/replace/rename) Medium PUL may be partially applied
Legacy update (update value, update insert) High Applied immediately, no batching
xmldb:store()/xmldb:remove() High Side effects immediate
sm:chmod()/sm:chown() Medium Permission changes immediate
file:write()/file:delete() High Cannot be rolled back

Mitigations:

  1. Warning dialog for queries containing update/store/delete/chmod/file expressions
  2. Admin-only kill — guest users can only abort the HTTP connection (server query runs to completion)
  3. Preclaiming locks (eXist-db PR #6112) prevents deadlocks during partial update application
  4. Detection regex covers XQUF, legacy update, xmldb:*, sm:*, file:* mutations
  5. Monitor sidebar shows all running queries server-wide with per-query kill buttons

14. Results toolbar and menubar polish

  • Copy all results: items are now separated by newlines when pasted
  • Number-of-results dropdown: changing the page size now immediately re-fetches from page 1 (previously only updated the internal variable)
  • Active toggle buttons: Indent and Highlight Matches buttons now show a solid blue background when active, clearly distinct from the hover state
  • Menubar toggle: clicking an already-open menu header now closes it, consistent with standard menubar behaviour

Temporary workarounds (remove after upstream merges)

  • LSP test skips: Autocomplete and outline Cypress tests are skipped when lsp:* module is not available. Remove once [feature] Add LSP XQuery function module exist#6130 is merged.
  • XQuery 4.0 Prettier guard: Formatting rejects xquery version "4.0" code with a user-friendly message. Remove once prettier-plugin-xquery adds 4.0 support.
  • Dynamic file module import: admin.xqm and sync.xqm use util:eval to dynamically load the file module. Can be simplified once eXist-db standardizes on the EXPath File module namespace.
  • Error line fallback: compileError parses line/column from error message text when lsp:diagnostics() returns line 0. Remove once lsp:diagnostics() line fix is deployed.
  • function-lookup fallback: query.xqm uses function-lookup for 4-arity lsp:fetch when the new overload isn't available. Remove once exist-lsp with serialization parameter support is deployed.

Known limitations

  • lsp:hover() does not return info for all registered module functions (e.g., sm:is-dba). Filed as server-side issue.
  • XQuery 4.0 parser is lazy-loaded as a separate 664KB bundle to avoid doubling the main bundle size.
  • Query evaluation is outside the LSP spec's scopelsp:eval/lsp:fetch/lsp:close are eXist-specific IDE support functions in the exist-lsp package. Reviewers are invited to weigh in on whether this scope is appropriate or if a separate package would be preferred.

Requirements

  • eXist-db 7.0+ with exist-lsp package installed
  • Roaster 1.8.0+

Test plan

  • Build and install XAR (npm run build && xst package install local build/eXide-3.5.5.xar --force)
  • Unit tests pass (npm test — 178 tests)
  • Cypress E2E tests pass (213 tests: 201 pass, 11 pending, 1 flaky monitor kill)
  • Full smoke test — 213 Cypress tests cover the vast majority; remaining visual/manual items tracked for future sessions
  • All LSP endpoints working — autocomplete_spec (11), diagnostics_panel_spec (5), error_status_spec (8), native_autocomplete_spec (7)
  • Cmd+Click go-to-definition with centered scroll and flash — goto_references_spec (F3 jump + clickable class)
  • Find All References (Shift+F3) with QuickPicker — goto_references_spec (QuickPicker opens + Escape closes)
  • Auto-triggered completions with prefix/unprefixed namespace support — autocomplete_spec covers prefixed, unprefixed, fn:, repo:, variable completions
  • Dark mode with One Dark syntax highlighting — layout_spec (dark mode layout)
  • Prettier formatting for XQuery 3.1, XML, HTML, CSS, JS, Markdown — prettier_format_spec (4 tests)
  • Outline panel with nested tree, XPath signatures, and filter — outline_navigation_spec (12 tests)
  • Tag matching, scope breadcrumb, selection match highlighting — tag_scope_selection_spec (CSS defined, XML/XQuery scope, selection match)
  • exist-next Docker compatibility verified — all development and testing done against exist-next Docker (eXist 7.0.0-SNAPSHOT)
  • Query execution with cursor pagination — query_execution_spec "paginates results with next/previous buttons"
  • Large result set (1 to 10000) — query_execution_spec "handles large result sets" (10 DOM nodes for 10K items)
  • Change serialization mode — query_execution_spec "re-fetches page when serialization mode changes without re-executing"
  • Toggle indent/highlight-matches — query_execution_spec "re-fetches page when indent toggle changes"
  • Cancel query with warning dialog — query_execution_spec cancel button tests (existence, visibility)
  • Monitor sidebar kill button — monitor_spec "shows running query and allows killing it" (flaky timing)

🤖 Generated with Claude Code

joewiz and others added 5 commits March 14, 2026 21:56
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>
@joewiz joewiz force-pushed the pr/modernize-eXide branch 5 times, most recently from 569033d to c226471 Compare March 16, 2026 00:25
joewiz and others added 5 commits March 16, 2026 08:40
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>
joewiz and others added 2 commits March 16, 2026 09:45
eXide Desktop wraps eXide's web UI in a native desktop application
via Tauri v2. In dev mode, it loads from the local eXist-db server;
in production, it bundles the web build.

- src-tauri/: Rust entry point, Tauri config, default icons
- npm run dev:desktop: opens native window against localhost:8080
- npm run build:desktop: produces .dmg/.exe/.AppImage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tauri-plugin-fs and tauri-plugin-dialog (Rust + JS)
- Register plugins in lib.rs, enable permissions in capabilities
- Add src/desktop-bridge.js: detects Tauri runtime and exposes
  readDir, readFile, writeFile, pickFolder, pickFile, pickSaveFile
- No-op in browser mode (eXide.desktop.isDesktop = false)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread .github/workflows/ci.yml
- name: Build frontend Using node.js ${{ matrix.node-version }} and build EXPath Package
run: npm run build

- name: Download Roaster dependency
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend using gh cli, and download both latest release and the min version as per expath-pkg.xml in a matrix

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Done in commits ea7d771–1ddca80: replaced curl with gh release download (using the built-in GITHUB_TOKEN) for the Roaster dependency. The existing release/latest Docker matrix covers the most important cases — testing against the specific semver-min from expath-pkg.xml (6.1.0) would require an old Docker image that predates the Roaster/lsp:* dependencies this PR needs. Happy to add that as a separate matrix entry once we settle on the minimum version the full feature set requires.

* #error-status. We need to wait for that to settle before testing.
*/
function waitForInitialLoad() {
cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

random timeouts are usually an anti pattern, is there something we can put on a named route, so we can wait on a request and its response?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Line 22 is already cy.wait('@compile', { timeout: 10000 }) — a named intercept alias set up in beforeEach via cy.intercept('POST', '**/api/query/compile').as('compile'). The remaining cy.wait(50) at line 30 is waiting for a browser MutationObserver microtask to fire after writing to #error-status, which can't be replaced with a network route. It's a minimal delay for a synchronous DOM write, not a timing guess.

const el = win.document.getElementById('error-status')
el.textContent = ''
})
cy.wait(50)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.a.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Same as above — this is the MutationObserver microtask wait (50ms), not a network timing guess.

cy.loginXHR('admin', '')
cy.visit('/eXide/index.html')
cy.reload(true)
cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.a.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

These lines are cy.visit() and cy.get('.path').should('contain', 'untitled-1') — no cy.wait() here. The numeric waits that were the real issue (lines 95/100/135/163/168/195/200) have been replaced with cy.intercept('POST', '**/api/query/completions').as('completions') + cy.wait('@completions') in commit 24ef119.

cy.reload(true)
cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
// Wait longer for login to propagate
cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.a.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

These lines are cy.visit() and cy.get('.path').should('contain', 'untitled-1') — no cy.wait() here. The numeric waits that were the real issue (lines 95/100/135/163/168/195/200) have been replaced with cy.intercept('POST', '**/api/query/completions').as('completions') + cy.wait('@completions') in commit 24ef119.

Comment thread src/deployment.js
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this covered in our tests?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Not yet — the deployment dialog involves a multi-step UI (selecting server, entering credentials, triggering package install) that's harder to automate without a test server with the public repo accessible. Tracked in the future test gaps list.

Comment thread src/dnd.js
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol for a moment I was wondering why we had a dungeons and dragons file

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

dnd.js is for Drag and Drop, not Dungeons and Dragons — though XQuery dungeon crawling is admittedly an underexplored genre.

Comment thread tools/REx.class Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Removed in commit ea7d771. The .class file is a compiled Java binary for the REx parser generator — it shouldn't have been in version control. generate-parser.js now compiles it from REx.java on demand via javac if the class file is absent.

Comment thread tools/REx.java
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we store and commit binaries and java here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

REx.java is the source for the REx parser generator (by Gunther Rademacher, bottlecaps.de), included for transparency and reproducible builds — the site hosts the tool but isn't always reachable. The compiled REx.class has been removed from git (commit ea7d771); npm run generate-parser now compiles it on first use via javac. The .java source stays so contributors can inspect and rebuild without depending on an external download.

Comment thread expath-pkg.xml Outdated
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://expath.org/ns/pkg" name="http://exist-db.org/apps/eXide" abbrev="eXide" version="3.5.5" spec="1.0">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.5.5 this should likely by 3.6.0 or 4.0.0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Bumped to 3.6.0 in commit a23c9cf. This is a significant feature release but preserves installation compatibility with the existing expath-pkg / repo:install flow, so a minor version bump feels right.

joewiz and others added 2 commits March 16, 2026 14:30
eXide Desktop wraps eXide's web UI in a native desktop application
via Tauri v2. Connects to a running eXist-db server for full
functionality (query execution, LSP, file management).

Features:
- Native macOS/Windows/Linux desktop app
- Custom app icon: colorful glass marbles on wood (Gemini design)
- Filesystem and dialog Tauri plugins for future local file access
- Desktop bridge JS module (detects Tauri, exposes native APIs)
- Release build navigates to eXist-db server URL (configurable
  via EXIDE_SERVER env var, defaults to localhost:8080)
- Dev mode loads from localhost:8080 with hot reload
- build-desktop.js: XHTML→HTML5 conversion for WebKit compatibility
- Injects eXide.configuration defaults for standalone rendering

Build:
- npm run dev:desktop — development with hot reload
- npm run build:desktop — produces .app/.dmg/.exe/.AppImage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On startup, shows a connection dialog where users can enter an
eXist-db server URL. Saves recent servers in localStorage.
Clicking Connect navigates directly to the server.

- Tauri store plugin for persistent preferences
- connect.html: sleek dark-themed connection UI with recent servers
- Defaults to localhost:8080/exist/apps/eXide
- EXIDE_SERVER env var skips the dialog for automated setups

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@duncdrum duncdrum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I m starting to be a bit concerned about a mix of Java, Rust, Js,… it adds quite a bit of complexity.

That being said love tauri.

Should be build on ci though.

Comment thread package.json Outdated
"clean": "node scripts/build.js clean",
"xqlint": "cd support/xqlint && node r.js -o build.js && cd ../..",
"cypress": "cypress run"
"test": "node --test test/parser-test.js test/visitor-test.js test/utils-test.js test/static-analysis-test.js test/snippet-test.js test/parser-registry-test.js test/xq40-syntax-test.js",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think node --test should suffice here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[This response was co-authored with Claude Code. -Joe]

Done in commit 1ddca80: simplified to node --test. Node 20+ auto-discovers *-test.js files in test/.

joewiz and others added 7 commits March 16, 2026 21:52
Full native menu bar matching eXide's web menus:
- eXide Desktop: About, Preferences (Cmd+,), Hide, Quit
- File: New XQuery (Cmd+N), New (Cmd+Shift+N), Open, Save, Close (Cmd+W)
- Edit: Undo/Redo, Cut/Copy/Paste, Find/Replace, Format, Toggle Comment
- View: Toggle Collections, Results, Dark Mode
- Navigate: Command Palette, Go to Definition (F3), Find References
  (Shift+F3), Go to Symbol, Go to Line, Function Doc, Diagnostics
- Run: Run Query (Cmd+Enter), Launch Application
- XQuery: Expand Selection, Rename, Extract Function/Variable
- App: Launch, Deploy, Download, Synchronize
- Window: Minimize, Maximize, Fullscreen, Next/Prev Tab
- Help: Keyboard Shortcuts, About eXide

Menu events execute JS in the webview via window.eval().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
File > Open Local Folder opens a native macOS folder picker, then
displays a LOCAL tab in the west panel with a browsable file tree
(pre-loaded 3 levels deep from Rust). Folders expand/collapse on click.

Architecture: since Tauri's JS IPC isn't available on server-loaded
pages, all filesystem I/O is done Rust-side. The directory tree is
serialized as JSON and injected via window.eval() into the eXide page.

Known limitations (future work):
- File opening on double-click not yet implemented (needs a mechanism
  for JS to request file content from Rust without IPC)
- LOCAL tab styling needs polish to match COLLECTIONS/OUTLINE
- Deep directories beyond 3 levels aren't pre-loaded

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking a file in the local filesystem pane now opens it in the
editor. Uses a Tauri custom URI scheme protocol (localfs://read/path)
so the webview can fetch local file contents without IPC.

Rust serves the file content with appropriate MIME types. The JS
creates a new editor tab with the file content and correct syntax mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Local files: update tab label to show filename instead of untitled-N
- Connect page: test server reachability with 8-second timeout before
  navigating. Shows Cancel button during connection, Retry on failure.
  Handles unreachable servers gracefully.

Co-Authored-By: Claude Opus 4.6 <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>
- Auto-connect to ws://host/exist/ws on eXide startup
- Monitor: try WebSocket streaming before falling back to HTTP polling
  (listens for exist/metrics, exist/queryStarted, exist/queryEnded)
- LSP hover: use WebSocket request/response when connected, HTTP
  fallback when not
- WebSocket URL derived from page location using monex's pattern
  (rootContext + "/ws")

Requires eXist-db with WebSocket support (PR #6145).
Graceful fallback to HTTP when WebSocket unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ignore plain-text "ping" keepalive messages from eXist-db WebSocket
  (server sends "ping" not JSON, was causing parse errors)
- Add 5 Cypress tests: auto-connect, endpoint connection, ping
  handling, API surface, graceful fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
joewiz and others added 8 commits March 18, 2026 15:50
…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>
…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>
Cancel button now aborts the in-flight HTTP request and attempts to kill
the server-side query via the admin API. If the query contains update
expressions (update, insert, delete, replace, rename), a confirmation
dialog warns about potential database inconsistency before cancelling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The side-effect detection regex now covers:
- XQUF: update, insert, delete, replace, rename
- xmldb: store, remove, create-collection, copy, move, rename
- sm: chmod, chown, chgrp, add-account, remove-account
- file: write, delete, move, mkdirs, serialize

Also hides cancel button and shows "Query cancelled." message after
cancel completes, and adds explanatory comments about the client-side
vs server-side cancel behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Serialization mode, indent, and highlight-matches settings from eXide's
results toolbar are now sent with each page fetch request. Changing these
settings re-fetches the current page with new serialization — no query
re-execution needed.

The results endpoint accepts all W3C serialization parameters as query
string params (method, indent, omit-xml-declaration, encoding, etc.)
plus eXist's highlight-matches. Falls back gracefully to 3-arity
lsp:fetch if the 4-arity overload isn't available yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New Cypress tests:
- Pagination: next/previous/first/last with 25-item result set
- Large result set: 10,000 items renders only 10 in DOM
- Serialization mode change: re-fetches page without re-executing
- Indent toggle: re-fetches page with new indent setting

Also fixes toggle button change event dispatch — programmatic
checkbox.checked changes now fire the change event so cursor
re-fetch listeners trigger correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pe, and selection match

New test files:
- goto_references_spec.cy.js (4 tests): F3 go-to-definition jumps to
  declaration, Cmd/Ctrl adds clickable class, findReferences opens
  QuickPicker, Escape closes QuickPicker
- tag_scope_selection_spec.cy.js (6 tests): tag matching CSS defined,
  XML scope breadcrumb shows XPath, XQuery scope shows function context,
  scope hidden outside named context, selection match highlights
  occurrences, highlights clear when selection clears

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Displays phase-level timing from lsp:eval: Compile, Eval, Total, Items.
Falls back to simple Elapsed display if the timing map isn't available
(e.g., older exist-lsp without the timing breakdown).

Also passes timing map through query.xqm execute endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@line-o line-o added this to v7.0.0 Mar 23, 2026
@line-o line-o self-requested a review March 30, 2026 18:12
@line-o line-o moved this to In review in v7.0.0 Mar 30, 2026
joewiz and others added 10 commits April 4, 2026 09:16
…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>
The lsp:* functions have been consolidated into the exist-api package
(http://exist-db.org/pkg/api) which provides a unified platform API
for eXist-db.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Used by Node.js test scripts to verify the /ws/eval endpoint protocol
(eval, cancel, progress messages) outside of the browser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This is a major feature release: CM6 editor, REx parser, Roaster REST API,
LSP integration, WebSocket monitoring, and Prettier formatting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The LGPL header was accidentally dropped when syncing the schema from
eXist-db/exist. Restored from the upstream source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before: before() and after() in file_save_spec each duplicated the same
XQuery cleanup request. Now both call cy.cleanupTestFiles(), and other
specs that create /db files can reuse it without duplication.

Co-Authored-By: Claude Sonnet 4.6 <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>
REx.class is a compiled Java binary — it shouldn't be in version control.
REx.java (the source, by Gunther Rademacher, bottlecaps.de) stays committed
for transparency and reproducible builds. generate-parser.js now compiles
REx.java via javac if the class file is absent, so the workflow is unchanged:
run `npm run generate-parser` and it just works.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Run `npm test` (node --test) before building — catches JS regressions early
- Validate OpenAPI schema with @redocly/cli before deploying
- Replace curl with `gh release download` for the Roaster dependency,
  using the built-in GITHUB_TOKEN so no secrets are needed
- Simplify package.json test script to `node --test` (file discovery is
  automatic for the *-test.js naming convention used in test/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@joewiz joewiz force-pushed the pr/modernize-eXide branch from a9ce2ba to 1ddca80 Compare April 13, 2026 01:48
joewiz and others added 8 commits April 12, 2026 21:55
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>
Previously, changing the number-of-results dropdown only updated the
internal variable without refreshing the display. Now it resets to
page 1 and re-fetches with the new page size immediately.

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>
xmldb:copy-collection() returns the destination path as a string.
The copy action was returning this string alongside the status map,
producing a 2-item sequence that roaster cannot serialize as JSON
(err:SERE0023). Wrap the for loop in let $_ := to discard the
return value and return only the status map.

Fixes the Cypress DB manager copy test and makes copy/paste of
collections and resources functional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replacing cy.wait(300) with cy.wait('@Completions') waits for the
network response but not for CM6's internal state to settle. By
the time acceptCompletion() or {enter} ran, the popup had been
dismissed. Replace with li[aria-selected="true"].click() which
interacts directly with the visible popup item before it can close.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eXide previously used its own /api/query endpoints. Route all query
calls to exist-api instead — cookie auth works because both apps share
the org.exist.login cookie domain (fixed in exist-api's cookieAuth spec).

eXide.js changes:
- POST/DELETE/GET URLs: api/query → ../exist-api/api/query
- Request body: base → module-load-path
- POST response: data.id → data.cursor, data.count → data.items
- GET results: data.items (wrapped) → data (plain array)

Version bumped to 4.0.0 to signal the new exist-api dependency, which
is already declared in expath-pkg.xml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
query_execution_spec.cy.js: two tests verifying eXide renders adaptive
output correctly — xs:string is double-quoted, xs:integer is bare.

adaptive_serialization_spec.cy.js: 16 UI-level tests covering all major
XDM atomic types drawn from XQTS ser/method-adaptive.xml test cases:
xs:integer, xs:decimal, xs:double, xs:float, xs:boolean, xs:string
(with internal quote doubling), xs:anyURI, xs:untypedAtomic,
xs:dateTime, xs:date, xs:time, xs:duration, xs:base64Binary,
xs:hexBinary, xs:QName.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

3 participants