bridge-syntax-highlight 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.depcheckrc +7 -0
- package/.vscode/launch.json +12 -0
- package/.vscode/tasks.json +12 -0
- package/.vscodeignore +7 -0
- package/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/build.mjs +57 -0
- package/language-configuration.json +28 -0
- package/logo128.png +0 -0
- package/package.json +61 -0
- package/src/extension.ts +51 -0
- package/src/server.ts +137 -0
- package/syntaxes/bridge.tmLanguage.json +279 -0
- package/tsconfig.json +16 -0
package/.depcheckrc
ADDED
package/.vscodeignore
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to the "bridge" extension will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.5.0] - 2026-02-21
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Language Server** with real-time diagnostics and hover information
|
|
10
|
+
- Syntax error diagnostics powered by Chevrotain parser with error recovery (partial AST even on broken files)
|
|
11
|
+
- Semantic diagnostics: undeclared handles, unsatisfied `with` dependencies
|
|
12
|
+
- Hover provider: hover over any handle, dependency, or wire name to see its type and declaration context
|
|
13
|
+
|
|
14
|
+
## [0.0.1] - 2026-02-21
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Syntax highlighting for Bridge language version 1.5
|
|
19
|
+
- Support for all top-level blocks: `version`, `const`, `tool`, `define`, `bridge`
|
|
20
|
+
- Wire operator highlighting: `<-`, `||`, `??`, `catch`, `:`
|
|
21
|
+
- Array mapping syntax: `source[] as iter { … }`
|
|
22
|
+
- Constant assignment highlighting: `.property = value`
|
|
23
|
+
- Built-in handle highlighting: `input`, `output`, `context`
|
|
24
|
+
- HTTP method constants: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, etc.
|
|
25
|
+
- Unquoted URL path values (e.g. `/search`, `/forecast`)
|
|
26
|
+
- `on error` fallback declarations
|
|
27
|
+
- Block brace matching
|
|
28
|
+
- Comment support (`#` line comments)
|
|
29
|
+
- String, number, and boolean literal highlighting
|
|
30
|
+
- `language-configuration.json` for bracket matching and comment toggling
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stackables
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# The Bridge Language — VS Code Extension
|
|
2
|
+
|
|
3
|
+
Full IDE support for [The Bridge](https://github.com/stackables/bridge): a declarative dataflow language for GraphQL. Wire data between APIs and schema fields using `.bridge` files — no resolvers, no codegen, no plumbing.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### Language Server (LSP)
|
|
10
|
+
|
|
11
|
+
- **Real-time diagnostics** — syntax errors and semantic issues highlighted as you type
|
|
12
|
+
- **Semantic validation** — undeclared handles, unsatisfied `with` dependencies, and unknown wire targets flagged immediately
|
|
13
|
+
- **Hover information** — hover over any handle, dependency, or declaration to see its type and source
|
|
14
|
+
- Bridge hover: type/field name, handle count, wire count
|
|
15
|
+
- Tool hover: function name, deps, wires
|
|
16
|
+
- Define hover: subgraph details
|
|
17
|
+
- Const hover: name and raw value
|
|
18
|
+
- Error recovery — partial AST is built even on broken files, so diagnostics remain accurate while you're mid-edit
|
|
19
|
+
|
|
20
|
+
### Syntax Highlighting
|
|
21
|
+
|
|
22
|
+
- Full syntax highlighting for `.bridge` files (language version 1.5)
|
|
23
|
+
- Block keyword highlighting: `version`, `const`, `tool`, `define`, `bridge`
|
|
24
|
+
- Wire operator colouring: `<-` (pull), `||` (falsy-coalesce), `??` (nullish gate), `catch` (error boundary), `:` (pipe)
|
|
25
|
+
- `force <handle>` and `force <handle> catch null` keyword highlighting
|
|
26
|
+
- Distinct colours for GraphQL type/field targets in `bridge` declarations
|
|
27
|
+
- Tool handle and alias highlighting
|
|
28
|
+
- Built-in handle highlighting: `input`, `output`, `context`
|
|
29
|
+
- Constant assignment colouring: `.property = value`
|
|
30
|
+
- HTTP method constants: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`
|
|
31
|
+
- Unquoted URL path values (e.g. `/search`, `/api/v1/forecast`)
|
|
32
|
+
- Array mapping syntax: `source[] as iter { … }`
|
|
33
|
+
- `on error` fallback declarations inside `tool` blocks
|
|
34
|
+
- String, number, boolean, and `null` literals
|
|
35
|
+
|
|
36
|
+
### Editor Integration
|
|
37
|
+
|
|
38
|
+
- Line comment toggling (`#`)
|
|
39
|
+
- Bracket matching for `{}` and `[]`
|
|
40
|
+
- Automatic language detection for `*.bridge` files
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Search for **"The Bridge Language"** in the VS Code Extensions panel, or install from the terminal:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
code --install-extension stackables.bridge-syntax-highlight
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Files named `*.bridge` are automatically detected.
|
|
51
|
+
|
|
52
|
+
## Related
|
|
53
|
+
|
|
54
|
+
- [The Bridge runtime](https://github.com/stackables/bridge) — `@stackables/bridge` on npm
|
|
55
|
+
- [Language reference](https://github.com/stackables/bridge/blob/main/docs/bridge-language-guide.md) — full syntax documentation
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Release Notes
|
|
60
|
+
|
|
61
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
package/build.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild bundler for the Bridge Language extension.
|
|
3
|
+
*
|
|
4
|
+
* Produces two outputs:
|
|
5
|
+
* build/extension.js — VS Code extension host entry (CJS, vscode external)
|
|
6
|
+
* build/server.js — Language server process (CJS, fully self-contained)
|
|
7
|
+
*
|
|
8
|
+
* Run: node build.mjs
|
|
9
|
+
* Watch: node build.mjs --watch
|
|
10
|
+
*/
|
|
11
|
+
import * as esbuild from "esbuild";
|
|
12
|
+
|
|
13
|
+
const isWatch = process.argv.includes("--watch");
|
|
14
|
+
|
|
15
|
+
const sharedOptions = {
|
|
16
|
+
bundle: true,
|
|
17
|
+
format: "cjs",
|
|
18
|
+
platform: "node",
|
|
19
|
+
target: "node18",
|
|
20
|
+
sourcemap: true,
|
|
21
|
+
minify: false,
|
|
22
|
+
logLevel: "info",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (isWatch) {
|
|
26
|
+
// Watch mode: two separate contexts
|
|
27
|
+
const [ctxExt, ctxSrv] = await Promise.all([
|
|
28
|
+
esbuild.context({
|
|
29
|
+
...sharedOptions,
|
|
30
|
+
entryPoints: ["src/extension.ts"],
|
|
31
|
+
outfile: "build/extension.js",
|
|
32
|
+
external: ["vscode"],
|
|
33
|
+
}),
|
|
34
|
+
esbuild.context({
|
|
35
|
+
...sharedOptions,
|
|
36
|
+
entryPoints: ["src/server.ts"],
|
|
37
|
+
outfile: "build/server.js",
|
|
38
|
+
}),
|
|
39
|
+
]);
|
|
40
|
+
await Promise.all([ctxExt.watch(), ctxSrv.watch()]);
|
|
41
|
+
console.log("Watching for changes…");
|
|
42
|
+
} else {
|
|
43
|
+
await Promise.all([
|
|
44
|
+
esbuild.build({
|
|
45
|
+
...sharedOptions,
|
|
46
|
+
entryPoints: ["src/extension.ts"],
|
|
47
|
+
outfile: "build/extension.js",
|
|
48
|
+
external: ["vscode"],
|
|
49
|
+
}),
|
|
50
|
+
esbuild.build({
|
|
51
|
+
...sharedOptions,
|
|
52
|
+
entryPoints: ["src/server.ts"],
|
|
53
|
+
outfile: "build/server.js",
|
|
54
|
+
}),
|
|
55
|
+
]);
|
|
56
|
+
console.log("Build complete.");
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comments": {
|
|
3
|
+
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
|
4
|
+
"lineComment": "#"
|
|
5
|
+
},
|
|
6
|
+
// symbols used as brackets
|
|
7
|
+
"brackets": [
|
|
8
|
+
["{", "}"],
|
|
9
|
+
["[", "]"],
|
|
10
|
+
["(", ")"]
|
|
11
|
+
],
|
|
12
|
+
// symbols that are auto closed when typing
|
|
13
|
+
"autoClosingPairs": [
|
|
14
|
+
["{", "}"],
|
|
15
|
+
["[", "]"],
|
|
16
|
+
["(", ")"],
|
|
17
|
+
["\"", "\""],
|
|
18
|
+
["'", "'"]
|
|
19
|
+
],
|
|
20
|
+
// symbols that can be used to surround a selection
|
|
21
|
+
"surroundingPairs": [
|
|
22
|
+
["{", "}"],
|
|
23
|
+
["[", "]"],
|
|
24
|
+
["(", ")"],
|
|
25
|
+
["\"", "\""],
|
|
26
|
+
["'", "'"]
|
|
27
|
+
]
|
|
28
|
+
}
|
package/logo128.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bridge-syntax-highlight",
|
|
3
|
+
"displayName": "The Bridge Language",
|
|
4
|
+
"description": "Syntax highlighting, real-time diagnostics, and hover information for the Bridge declarative dataflow language",
|
|
5
|
+
"version": "0.8.0",
|
|
6
|
+
"publisher": "stackables",
|
|
7
|
+
"icon": "logo128.png",
|
|
8
|
+
"engines": {
|
|
9
|
+
"vscode": "^1.109.0"
|
|
10
|
+
},
|
|
11
|
+
"categories": [
|
|
12
|
+
"Programming Languages"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/stackables/bridge.git"
|
|
17
|
+
},
|
|
18
|
+
"main": "./build/extension.js",
|
|
19
|
+
"activationEvents": [
|
|
20
|
+
"onLanguage:bridge"
|
|
21
|
+
],
|
|
22
|
+
"contributes": {
|
|
23
|
+
"languages": [
|
|
24
|
+
{
|
|
25
|
+
"id": "bridge",
|
|
26
|
+
"aliases": [
|
|
27
|
+
"The Bridge",
|
|
28
|
+
"bridge"
|
|
29
|
+
],
|
|
30
|
+
"extensions": [
|
|
31
|
+
".bridge"
|
|
32
|
+
],
|
|
33
|
+
"configuration": "./language-configuration.json"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"grammars": [
|
|
37
|
+
{
|
|
38
|
+
"language": "bridge",
|
|
39
|
+
"scopeName": "source.bridge",
|
|
40
|
+
"path": "./syntaxes/bridge.tmLanguage.json"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.3.2",
|
|
46
|
+
"@types/vscode": "^1.109.0",
|
|
47
|
+
"esbuild": "^0.27.3",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vscode-languageclient": "^9.0.1",
|
|
50
|
+
"vscode-languageserver": "^9.0.1",
|
|
51
|
+
"vscode-languageserver-textdocument": "^1.0.12",
|
|
52
|
+
"@stackables/bridge": "1.22.1"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"prebuild": "pnpm --filter @stackables/bridge build",
|
|
56
|
+
"build": "node build.mjs",
|
|
57
|
+
"watch": "node build.mjs --watch",
|
|
58
|
+
"prevscode:prepublish": "pnpm --filter @stackables/bridge build",
|
|
59
|
+
"vscode:prepublish": "node build.mjs"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/extension.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VS Code extension client — activates and manages the Bridge Language Server.
|
|
3
|
+
*
|
|
4
|
+
* Runs inside the VS Code extension host. Its only job is to spawn the
|
|
5
|
+
* language server process and wire up the LSP client.
|
|
6
|
+
*/
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import { ExtensionContext } from "vscode";
|
|
9
|
+
import {
|
|
10
|
+
LanguageClient,
|
|
11
|
+
LanguageClientOptions,
|
|
12
|
+
ServerOptions,
|
|
13
|
+
TransportKind,
|
|
14
|
+
} from "vscode-languageclient/node";
|
|
15
|
+
|
|
16
|
+
let client: LanguageClient | undefined;
|
|
17
|
+
|
|
18
|
+
export function activate(context: ExtensionContext): void {
|
|
19
|
+
const serverModule = context.asAbsolutePath(path.join("build", "server.js"));
|
|
20
|
+
|
|
21
|
+
const serverOptions: ServerOptions = {
|
|
22
|
+
run: {
|
|
23
|
+
module: serverModule,
|
|
24
|
+
transport: TransportKind.ipc,
|
|
25
|
+
},
|
|
26
|
+
debug: {
|
|
27
|
+
module: serverModule,
|
|
28
|
+
transport: TransportKind.ipc,
|
|
29
|
+
options: { execArgv: ["--nolazy", "--inspect=6009"] },
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const clientOptions: LanguageClientOptions = {
|
|
34
|
+
// Only activate for .bridge files
|
|
35
|
+
documentSelector: [{ scheme: "file", language: "bridge" }],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
client = new LanguageClient(
|
|
39
|
+
"bridgeLanguageServer",
|
|
40
|
+
"Bridge Language Server",
|
|
41
|
+
serverOptions,
|
|
42
|
+
clientOptions,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
client.start();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function deactivate(): Thenable<void> | undefined {
|
|
49
|
+
if (!client) return undefined;
|
|
50
|
+
return client.stop();
|
|
51
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Language Server — provides LSP features for .bridge files.
|
|
3
|
+
*
|
|
4
|
+
* Runs as a standalone Node.js process spawned by the extension client.
|
|
5
|
+
* Communicates via IPC using the Language Server Protocol.
|
|
6
|
+
*
|
|
7
|
+
* This file is a thin adapter: all intelligence logic lives in
|
|
8
|
+
* `BridgeLanguageService` from @stackables/bridge. This server just
|
|
9
|
+
* maps its output to the LSP wire format.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* • Real-time diagnostics (syntax errors + semantic validation)
|
|
13
|
+
* • Hover information (handle bindings, bridge/tool declarations)
|
|
14
|
+
* • Autocomplete (built-in std.* / math.* tool names)
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
createConnection,
|
|
18
|
+
TextDocuments,
|
|
19
|
+
ProposedFeatures,
|
|
20
|
+
type InitializeResult,
|
|
21
|
+
TextDocumentSyncKind,
|
|
22
|
+
DiagnosticSeverity,
|
|
23
|
+
CompletionItemKind,
|
|
24
|
+
MarkupKind,
|
|
25
|
+
} from "vscode-languageserver/node";
|
|
26
|
+
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
27
|
+
import { BridgeLanguageService } from "@stackables/bridge";
|
|
28
|
+
import type { CompletionKind } from "@stackables/bridge";
|
|
29
|
+
|
|
30
|
+
// ── Connection & document manager ──────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
const connection = createConnection(ProposedFeatures.all);
|
|
33
|
+
const documents = new TextDocuments(TextDocument);
|
|
34
|
+
|
|
35
|
+
/** One language-service instance per open document (keyed by URI). */
|
|
36
|
+
const services = new Map<string, BridgeLanguageService>();
|
|
37
|
+
|
|
38
|
+
function getService(uri: string): BridgeLanguageService {
|
|
39
|
+
let svc = services.get(uri);
|
|
40
|
+
if (!svc) {
|
|
41
|
+
svc = new BridgeLanguageService();
|
|
42
|
+
services.set(uri, svc);
|
|
43
|
+
}
|
|
44
|
+
return svc;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
connection.onInitialize(
|
|
48
|
+
(): InitializeResult => ({
|
|
49
|
+
capabilities: {
|
|
50
|
+
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
51
|
+
hoverProvider: true,
|
|
52
|
+
completionProvider: {
|
|
53
|
+
triggerCharacters: ["."],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// ── Mapping helpers ────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
const completionKindMap: Record<CompletionKind, CompletionItemKind> = {
|
|
62
|
+
function: CompletionItemKind.Function,
|
|
63
|
+
variable: CompletionItemKind.Variable,
|
|
64
|
+
keyword: CompletionItemKind.Keyword,
|
|
65
|
+
type: CompletionItemKind.Class,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// ── Diagnostics ────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
function validate(doc: TextDocument): void {
|
|
71
|
+
const svc = getService(doc.uri);
|
|
72
|
+
svc.update(doc.getText());
|
|
73
|
+
|
|
74
|
+
const diags = svc.getDiagnostics();
|
|
75
|
+
|
|
76
|
+
connection.sendDiagnostics({
|
|
77
|
+
uri: doc.uri,
|
|
78
|
+
diagnostics: diags.map((d) => ({
|
|
79
|
+
severity:
|
|
80
|
+
d.severity === "error"
|
|
81
|
+
? DiagnosticSeverity.Error
|
|
82
|
+
: DiagnosticSeverity.Warning,
|
|
83
|
+
range: d.range,
|
|
84
|
+
message: d.message,
|
|
85
|
+
source: "bridge",
|
|
86
|
+
})),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
documents.onDidChangeContent((change) => validate(change.document));
|
|
91
|
+
documents.onDidOpen((e) => validate(e.document));
|
|
92
|
+
documents.onDidClose((e) => {
|
|
93
|
+
services.delete(e.document.uri);
|
|
94
|
+
connection.sendDiagnostics({ uri: e.document.uri, diagnostics: [] });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ── Hover ──────────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
connection.onHover((params) => {
|
|
100
|
+
const doc = documents.get(params.textDocument.uri);
|
|
101
|
+
if (!doc) return null;
|
|
102
|
+
|
|
103
|
+
const svc = getService(doc.uri);
|
|
104
|
+
svc.update(doc.getText());
|
|
105
|
+
|
|
106
|
+
const hover = svc.getHover(params.position);
|
|
107
|
+
if (!hover) return null;
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
contents: { kind: MarkupKind.Markdown, value: hover.content },
|
|
111
|
+
...(hover.range ? { range: hover.range } : {}),
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ── Completion ─────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
connection.onCompletion((params) => {
|
|
118
|
+
const doc = documents.get(params.textDocument.uri);
|
|
119
|
+
if (!doc) return [];
|
|
120
|
+
|
|
121
|
+
const svc = getService(doc.uri);
|
|
122
|
+
svc.update(doc.getText());
|
|
123
|
+
|
|
124
|
+
const completions = svc.getCompletions(params.position);
|
|
125
|
+
|
|
126
|
+
return completions.map((c) => ({
|
|
127
|
+
label: c.label,
|
|
128
|
+
kind: completionKindMap[c.kind] ?? CompletionItemKind.Text,
|
|
129
|
+
detail: c.detail,
|
|
130
|
+
documentation: c.documentation,
|
|
131
|
+
}));
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── Start ──────────────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
documents.listen(connection);
|
|
137
|
+
connection.listen();
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
|
3
|
+
"name": "bridge",
|
|
4
|
+
"scopeName": "source.bridge",
|
|
5
|
+
"patterns": [
|
|
6
|
+
{ "include": "#comments" },
|
|
7
|
+
{ "include": "#version-line" },
|
|
8
|
+
{ "include": "#block-headers" },
|
|
9
|
+
{ "include": "#block-braces" },
|
|
10
|
+
{ "include": "#force-lines" },
|
|
11
|
+
{ "include": "#wire-lines" },
|
|
12
|
+
{ "include": "#const-lines" },
|
|
13
|
+
{ "include": "#reserved-handles" },
|
|
14
|
+
{ "include": "#strings" },
|
|
15
|
+
{ "include": "#numbers" },
|
|
16
|
+
{ "include": "#booleans" },
|
|
17
|
+
{ "include": "#http-methods" },
|
|
18
|
+
{ "include": "#path-values" }
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
|
|
22
|
+
"version-line": {
|
|
23
|
+
"comment": "version 1.5",
|
|
24
|
+
"match": "^\\s*(version)\\s+(\\d+\\.\\d+)",
|
|
25
|
+
"captures": {
|
|
26
|
+
"1": { "name": "keyword.other.version.bridge" },
|
|
27
|
+
"2": { "name": "constant.numeric.version.bridge" }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"block-braces": {
|
|
32
|
+
"comment": "Optional block delimiters { }",
|
|
33
|
+
"patterns": [{
|
|
34
|
+
"name": "punctuation.definition.block.bridge",
|
|
35
|
+
"match": "[{}]"
|
|
36
|
+
}]
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
"comments": {
|
|
40
|
+
"patterns": [{
|
|
41
|
+
"name": "comment.line.number-sign.bridge",
|
|
42
|
+
"match": "#.*$"
|
|
43
|
+
}]
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
"http-methods": {
|
|
47
|
+
"comment": "HTTP method constants used as bare values",
|
|
48
|
+
"match": "\\b(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\\b",
|
|
49
|
+
"name": "constant.language.http-method.bridge"
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"path-values": {
|
|
53
|
+
"comment": "Unquoted slash-prefixed URL paths used as values (e.g. /search, /forecast)",
|
|
54
|
+
"match": "(?<==\\s*)/[A-Za-z0-9_/-]*",
|
|
55
|
+
"name": "string.unquoted.path.bridge"
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
"reserved-handles": {
|
|
59
|
+
"comment": "Special injected graph handles",
|
|
60
|
+
"match": "\\b(input|output|context)\\b",
|
|
61
|
+
"name": "variable.language.special.bridge"
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"barewords": {
|
|
65
|
+
"comment": "Uppercase barewords like EUR or USD",
|
|
66
|
+
"match": "\\b[A-Z_][A-Z0-9_]*\\b",
|
|
67
|
+
"name": "constant.other.enum.bridge"
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"block-headers": {
|
|
71
|
+
"comment": "Lines starting with bridge/define/tool/const/with keywords",
|
|
72
|
+
"patterns": [
|
|
73
|
+
{
|
|
74
|
+
"comment": "bridge Type.field with <target> (inline composition, no braces)",
|
|
75
|
+
"match": "^\\s*(bridge)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)(\\s+with\\s+)([A-Za-z_][A-Za-z0-9_.]*)",
|
|
76
|
+
"captures": {
|
|
77
|
+
"1": { "name": "keyword.control.bridge" },
|
|
78
|
+
"2": { "name": "entity.name.type.bridge" },
|
|
79
|
+
"3": { "name": "entity.name.function.bridge" },
|
|
80
|
+
"4": { "name": "keyword.control.bridge" },
|
|
81
|
+
"5": { "name": "entity.name.function.bridge" }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"comment": "bridge Type.field (block form)",
|
|
86
|
+
"match": "^\\s*(bridge)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)",
|
|
87
|
+
"captures": {
|
|
88
|
+
"1": { "name": "keyword.control.bridge" },
|
|
89
|
+
"2": { "name": "entity.name.type.bridge" },
|
|
90
|
+
"3": { "name": "entity.name.function.bridge" }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"comment": "define <name> (reusable sub-graph)",
|
|
95
|
+
"match": "^\\s*(define)\\s+([A-Za-z_][A-Za-z0-9_]*)",
|
|
96
|
+
"captures": {
|
|
97
|
+
"1": { "name": "keyword.control.bridge" },
|
|
98
|
+
"2": { "name": "entity.name.function.bridge" }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"comment": "tool <name> from <std.source>",
|
|
103
|
+
"match": "^\\s*(tool)\\s+([A-Za-z_][A-Za-z0-9_]*)(\\s+from\\s+)([A-Za-z_][A-Za-z0-9_.]*)",
|
|
104
|
+
"captures": {
|
|
105
|
+
"1": { "name": "keyword.control.bridge" },
|
|
106
|
+
"2": { "name": "variable.other.handle.bridge" },
|
|
107
|
+
"3": { "name": "keyword.control.bridge" },
|
|
108
|
+
"4": { "name": "entity.name.function.bridge" }
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"comment": "const name = ...",
|
|
113
|
+
"match": "^\\s*(const)\\s+([A-Za-z_][A-Za-z0-9_]*)",
|
|
114
|
+
"captures": {
|
|
115
|
+
"1": { "name": "keyword.control.bridge" },
|
|
116
|
+
"2": { "name": "variable.other.constant.bridge" }
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"comment": "with <tool> as <handle> / with input as i",
|
|
121
|
+
"match": "^\\s*(with)\\s+([A-Za-z_][A-Za-z0-9_.]*)(\\s+as\\s+)([A-Za-z_][A-Za-z0-9_]*)",
|
|
122
|
+
"captures": {
|
|
123
|
+
"1": { "name": "keyword.control.bridge" },
|
|
124
|
+
"2": { "name": "entity.name.function.bridge" },
|
|
125
|
+
"3": { "name": "keyword.control.bridge" },
|
|
126
|
+
"4": { "name": "variable.other.handle.bridge" }
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"comment": "with <tool> (no alias)",
|
|
131
|
+
"match": "^\\s*(with)\\s+([A-Za-z_][A-Za-z0-9_.]*)",
|
|
132
|
+
"captures": {
|
|
133
|
+
"1": { "name": "keyword.control.bridge" },
|
|
134
|
+
"2": { "name": "entity.name.function.bridge" }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"comment": "alias <source> as <name> (node alias / rename)",
|
|
139
|
+
"match": "^\\s*(alias)\\s+(.+?)\\s+(as)\\s+([A-Za-z_][A-Za-z0-9_]*)",
|
|
140
|
+
"captures": {
|
|
141
|
+
"1": { "name": "keyword.control.bridge" },
|
|
142
|
+
"2": { "name": "entity.name.function.bridge" },
|
|
143
|
+
"3": { "name": "keyword.control.bridge" },
|
|
144
|
+
"4": { "name": "variable.other.handle.bridge" }
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"comment": "on error = value / on error <- source",
|
|
149
|
+
"match": "^\\s*(on error)\\s*(<-|=)",
|
|
150
|
+
"captures": {
|
|
151
|
+
"1": { "name": "keyword.control.bridge" },
|
|
152
|
+
"2": { "name": "keyword.operator.wire.bridge" }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
"force-lines": {
|
|
159
|
+
"comment": "Force statement: force <handle> [catch null]",
|
|
160
|
+
"match": "^\\s*(force)\\s+([A-Za-z_][A-Za-z0-9_]*)(?:\\s+(catch)\\s+(null))?",
|
|
161
|
+
"captures": {
|
|
162
|
+
"1": { "name": "keyword.control.bridge" },
|
|
163
|
+
"2": { "name": "variable.other.handle.bridge" },
|
|
164
|
+
"3": { "name": "keyword.control.bridge" },
|
|
165
|
+
"4": { "name": "constant.language.null.bridge" }
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
"wire-lines": {
|
|
170
|
+
"comment": "Wire line: target <- source [|| …] [?? …] [catch …] — target may be dot-prefixed (.field) inside array-map blocks",
|
|
171
|
+
"begin": "^(\\s*)(\\.?[A-Za-z_][A-Za-z0-9_.]*)\\s*(<-)",
|
|
172
|
+
"end": "$",
|
|
173
|
+
"beginCaptures": {
|
|
174
|
+
"2": { "name": "variable.other.target.bridge" },
|
|
175
|
+
"3": { "name": "keyword.operator.wire.bridge" }
|
|
176
|
+
},
|
|
177
|
+
"patterns": [
|
|
178
|
+
{ "include": "#comments" },
|
|
179
|
+
{
|
|
180
|
+
"comment": "Array mapping: source[] as <alias>",
|
|
181
|
+
"match": "(\\[\\])(\\s+as\\s+)([A-Za-z_][A-Za-z0-9_]*)",
|
|
182
|
+
"captures": {
|
|
183
|
+
"1": { "name": "keyword.operator.array-map.bridge" },
|
|
184
|
+
"2": { "name": "keyword.control.bridge" },
|
|
185
|
+
"3": { "name": "variable.other.handle.bridge" }
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"comment": "Array index [N]",
|
|
190
|
+
"match": "\\[\\d+\\]",
|
|
191
|
+
"name": "keyword.operator.index.bridge"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"comment": "Pipe separator: colon between identifiers",
|
|
195
|
+
"match": ":(?=[A-Za-z_])",
|
|
196
|
+
"name": "keyword.operator.pipe.bridge"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"comment": "|| null-coalesce",
|
|
200
|
+
"match": "\\|\\|",
|
|
201
|
+
"name": "keyword.operator.null-coalesce.bridge"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"comment": "?? nullish gate",
|
|
205
|
+
"match": "\\?\\?",
|
|
206
|
+
"name": "keyword.operator.nullish.bridge"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"comment": "catch error boundary",
|
|
210
|
+
"match": "\\bcatch\\b",
|
|
211
|
+
"name": "keyword.control.bridge"
|
|
212
|
+
},
|
|
213
|
+
{ "include": "#reserved-handles" },
|
|
214
|
+
{ "include": "#strings" },
|
|
215
|
+
{ "include": "#numbers" },
|
|
216
|
+
{ "include": "#booleans" }
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
"const-lines": {
|
|
221
|
+
"comment": "Property/param assignments: .name = value or name = value. Hyphens allowed in path segments (e.g. .headers.User-Agent).",
|
|
222
|
+
"patterns": [
|
|
223
|
+
{
|
|
224
|
+
"comment": "Dot-prefixed config param: .property[.segment]* = value (hyphens allowed in segments)",
|
|
225
|
+
"begin": "^\\s*(\\.[A-Za-z_][A-Za-z0-9_-]*(?:\\.[A-Za-z_][A-Za-z0-9_-]*)*)\\s*(=)\\s*",
|
|
226
|
+
"end": "$",
|
|
227
|
+
"beginCaptures": {
|
|
228
|
+
"1": { "name": "variable.other.property.bridge" },
|
|
229
|
+
"2": { "name": "keyword.operator.assignment.bridge" }
|
|
230
|
+
},
|
|
231
|
+
"patterns": [
|
|
232
|
+
{ "include": "#comments" },
|
|
233
|
+
{ "include": "#barewords" },
|
|
234
|
+
{ "include": "#strings" },
|
|
235
|
+
{ "include": "#numbers" },
|
|
236
|
+
{ "include": "#booleans" },
|
|
237
|
+
{ "include": "#http-methods" },
|
|
238
|
+
{ "include": "#path-values" }
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"comment": "Regular param: property = value",
|
|
243
|
+
"begin": "^\\s*([A-Za-z_][A-Za-z0-9_.]*)\\s*(=)\\s*",
|
|
244
|
+
"end": "$",
|
|
245
|
+
"beginCaptures": {
|
|
246
|
+
"1": { "name": "variable.other.property.bridge" },
|
|
247
|
+
"2": { "name": "keyword.operator.assignment.bridge" }
|
|
248
|
+
},
|
|
249
|
+
"patterns": [
|
|
250
|
+
{ "include": "#comments" },
|
|
251
|
+
{ "include": "#barewords" },
|
|
252
|
+
{ "include": "#strings" },
|
|
253
|
+
{ "include": "#numbers" },
|
|
254
|
+
{ "include": "#booleans" },
|
|
255
|
+
{ "include": "#http-methods" },
|
|
256
|
+
{ "include": "#path-values" }
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
"strings": {
|
|
263
|
+
"name": "string.quoted.double.bridge",
|
|
264
|
+
"begin": "\"",
|
|
265
|
+
"end": "\"",
|
|
266
|
+
"patterns": [{ "name": "constant.character.escape.bridge", "match": "\\\\." }]
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
"numbers": {
|
|
270
|
+
"name": "constant.numeric.bridge",
|
|
271
|
+
"match": "-?\\b\\d+(\\.\\d+)?\\b"
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
"booleans": {
|
|
275
|
+
"name": "constant.language.bridge",
|
|
276
|
+
"match": "\\b(true|false|null)\\b"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"outDir": "build",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"moduleResolution": "node"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"],
|
|
15
|
+
"exclude": ["node_modules", "out"]
|
|
16
|
+
}
|