Skip to content

Add strict LSP-backed Bridge formatter with syntax-safe aborts and editor-config-aware indentation#99

Merged
aarne merged 4 commits intomainfrom
copilot/implement-lsp-formatting-request
Mar 6, 2026
Merged

Add strict LSP-backed Bridge formatter with syntax-safe aborts and editor-config-aware indentation#99
aarne merged 4 commits intomainfrom
copilot/implement-lsp-formatting-request

Conversation

Copy link
Contributor

Copilot AI commented Mar 6, 2026

This PR wires Bridge’s CST-based pretty-printer into the Language Server textDocument/formatting path so editors can format .bridge files natively. It also hardens formatter behavior for real-time editing by requiring syntactically valid input in strict mode and proving idempotency.

  • Strict formatter API for editor use

    • Added prettyPrintToSource(...) in bridge-parser with support for LSP formatting options:
      • tabSize: number
      • insertSpaces: boolean
    • Added parseBridgeCst(...) export for explicit CST parse validation.
    • Removed formatBridge(...) compatibility API/export.
    • Updated playground formatting to use the new strict formatter path (prettyPrintToSource) with a local safe wrapper to preserve UX while editing invalid drafts.
  • Formatter safety guarantees

    • Added unit coverage for:
      • Idempotency: formatting output is stable across repeated runs.
      • Syntax safety: strict formatter throws on invalid syntax, enabling safe abort behavior instead of rewriting partial code.
      • Indent options: verifies tab/space behavior based on editor options.
    • Updated formatter tests to use prettyPrintToSource, including shared test utilities for snippet-style formatting assertions.
  • Language Server formatting integration

    • onInitialize now advertises documentFormattingProvider: true.
    • Implemented connection.onDocumentFormatting:
      • fetches latest text from TextDocuments
      • parses to CST and aborts on syntax errors (warn + null)
      • formats with prettyPrintToSource(..., params.options)
      • returns a single full-document TextEdit.replace(...)
  • VS Code extension UX

    • Extension manifest now sets Bridge’s default formatter under [bridge].
    • README updated to announce formatting support and that it respects local tab/space settings and works with format-on-save.
connection.onDocumentFormatting((params) => {
  const doc = documents.get(params.textDocument.uri);
  if (!doc) return null;

  const text = doc.getText();
  try {
    const cst = parseBridgeCst(text);
    const formatted = prettyPrintToSource({ source: text, cst }, params.options);
    return [TextEdit.replace(Range.create({ line: 0, character: 0 }, doc.positionAt(text.length)), formatted)];
  } catch (error) {
    connection.console.warn(`Bridge formatting aborted due to syntax errors: ${error instanceof Error ? error.message : String(error)}`);
    return null;
  }
});

> [!WARNING]
>
>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@changeset-bot
Copy link

changeset-bot bot commented Mar 6, 2026

⚠️ No Changeset found

Latest commit: 16bbf70

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 6, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bridge 16bbf70 Commit Preview URL

Branch Preview URL
Mar 06 2026, 07:23 AM

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement LSP formatting request for Bridge DSL files Add strict LSP-backed Bridge formatter with syntax-safe aborts and editor-config-aware indentation Mar 6, 2026
Copilot finished work on behalf of aarne March 6, 2026 07:00
@aarne
Copy link
Contributor

aarne commented Mar 6, 2026

@copilot

don't keep anything for compatibility. formatBridge is used only internally in the playground. you can change that also to use the new code

…strict formatter

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 6, 2026

@copilot

don't keep anything for compatibility. formatBridge is used only internally in the playground. you can change that also to use the new code

Done in 16bbf70. I removed the formatBridge compatibility API/export and switched playground usage to prettyPrintToSource (with local try/catch fallback for invalid drafts), plus updated formatter tests accordingly.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.open-meteo.com
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA tal-transform-types --conditions source --test test/*.test.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test test/*.test.ts bash 0/x64/bin/node tal-transform-tysh (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test tACCEPT bash odules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh tal-transform-tysh (dns block)
  • dummyjson.com
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA tal-transform-types --conditions source --test test/*.test.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test test/*.test.ts bash 0/x64/bin/node tal-transform-tysh (dns block)
  • nominatim.openstreetmap.org
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA tal-transform-types --conditions source --test test/*.test.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test test/*.test.ts bash 0/x64/bin/node tal-transform-tysh (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test tACCEPT bash odules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh tal-transform-tysh (dns block)
  • transport.opendata.ch
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA tal-transform-types --conditions source --test test/*.test.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test test/*.test.ts bash de/node/bin/bash tal-transform-tysh (dns block)
  • v6.db.api.bahn.guru
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA tal-transform-types --conditions source --test test/*.test.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA source --test test/*.test.ts bash de/node/bin/bash tal-transform-tysh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of aarne March 6, 2026 07:18
@aarne aarne marked this pull request as ready for review March 6, 2026 07:19
@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

🐰 Bencher Report

Branchcopilot/implement-lsp-formatting-request
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
nanoseconds (ns)
(Result Δ%)
Upper Boundary
nanoseconds (ns)
(Limit %)
compiled: absolute baseline (passthrough, no tools)📈 view plot
🚷 view threshold
0.00 ns
(-1.03%)Baseline: 0.00 ns
0.00 ns
(91.49%)
compiled: array + tool-per-element 10📈 view plot
🚷 view threshold
0.01 ns
(-3.17%)Baseline: 0.01 ns
0.01 ns
(91.32%)
compiled: array + tool-per-element 100📈 view plot
🚷 view threshold
0.03 ns
(-4.83%)Baseline: 0.03 ns
0.04 ns
(89.86%)
compiled: chained 3-tool fan-out📈 view plot
🚷 view threshold
0.00 ns
(-0.79%)Baseline: 0.00 ns
0.00 ns
(92.88%)
compiled: flat array 10 items📈 view plot
🚷 view threshold
0.00 ns
(-1.57%)Baseline: 0.01 ns
0.01 ns
(92.31%)
compiled: flat array 100 items📈 view plot
🚷 view threshold
0.01 ns
(-0.24%)Baseline: 0.01 ns
0.01 ns
(94.58%)
compiled: flat array 1000 items📈 view plot
🚷 view threshold
0.07 ns
(+1.79%)Baseline: 0.07 ns
0.07 ns
(97.23%)
compiled: nested array 10x10📈 view plot
🚷 view threshold
0.02 ns
(-0.07%)Baseline: 0.02 ns
0.02 ns
(94.53%)
compiled: nested array 20x10📈 view plot
🚷 view threshold
0.03 ns
(-0.39%)Baseline: 0.03 ns
0.04 ns
(94.67%)
compiled: nested array 5x5📈 view plot
🚷 view threshold
0.01 ns
(-0.70%)Baseline: 0.01 ns
0.01 ns
(93.89%)
compiled: short-circuit (overdefinition bypass)📈 view plot
🚷 view threshold
0.00 ns
(-1.43%)Baseline: 0.00 ns
0.00 ns
(91.27%)
compiled: simple chain (1 tool)📈 view plot
🚷 view threshold
0.00 ns
(-1.10%)Baseline: 0.00 ns
0.00 ns
(91.55%)
exec: absolute baseline (passthrough, no tools)📈 view plot
🚷 view threshold
0.00 ns
(+4.77%)Baseline: 0.00 ns
0.00 ns
(85.64%)
exec: array + tool-per-element 10📈 view plot
🚷 view threshold
0.08 ns
(+1.48%)Baseline: 0.08 ns
0.10 ns
(76.79%)
exec: array + tool-per-element 100📈 view plot
🚷 view threshold
0.68 ns
(-0.34%)Baseline: 0.68 ns
0.92 ns
(73.37%)
exec: chained 3-tool fan-out📈 view plot
🚷 view threshold
0.02 ns
(+12.96%)Baseline: 0.01 ns
0.02 ns
(83.66%)
exec: flat array 10 items📈 view plot
🚷 view threshold
0.01 ns
(-8.67%)Baseline: 0.02 ns
0.03 ns
(40.96%)
exec: flat array 100 items📈 view plot
🚷 view threshold
0.07 ns
(-23.48%)Baseline: 0.10 ns
0.31 ns
(23.71%)
exec: flat array 1000 items📈 view plot
🚷 view threshold
0.66 ns
(-31.85%)Baseline: 0.96 ns
3.74 ns
(17.52%)
exec: nested array 10x10📈 view plot
🚷 view threshold
0.12 ns
(-16.18%)Baseline: 0.14 ns
0.38 ns
(32.03%)
exec: nested array 20x10📈 view plot
🚷 view threshold
0.23 ns
(-17.54%)Baseline: 0.28 ns
0.77 ns
(30.37%)
exec: nested array 5x5📈 view plot
🚷 view threshold
0.05 ns
(-8.89%)Baseline: 0.05 ns
0.11 ns
(44.64%)
exec: short-circuit (overdefinition bypass)📈 view plot
🚷 view threshold
0.00 ns
(+6.86%)Baseline: 0.00 ns
0.00 ns
(94.12%)
exec: simple chain (1 tool)📈 view plot
🚷 view threshold
0.01 ns
(+11.45%)Baseline: 0.01 ns
0.01 ns
(86.14%)
parse: large bridge (20 handles x 5 wires)📈 view plot
🚷 view threshold
1.10 ns
(+11.65%)Baseline: 0.99 ns
1.35 ns
(81.29%)
parse: simple bridge📈 view plot
🚷 view threshold
0.04 ns
(+18.77%)Baseline: 0.03 ns
0.05 ns
(83.45%)
🐰 View full continuous benchmarking report in Bencher

@aarne aarne merged commit f754272 into main Mar 6, 2026
11 of 12 checks passed
@aarne aarne deleted the copilot/implement-lsp-formatting-request branch March 6, 2026 07:24
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