@flux-lang/vscode-flux 0.1.7

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/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Flux VS Code Extension
2
+ [![VS Code Marketplace](https://img.shields.io/visual-studio-marketplace/v/cbassuarez.flux-language-support)](https://marketplace.visualstudio.com/items?itemName=cbassuarez.flux-language-support)
3
+ [![VS Code Installs](https://img.shields.io/visual-studio-marketplace/i/cbassuarez.flux-language-support)](https://marketplace.visualstudio.com/items?itemName=cbassuarez.flux-language-support)
4
+ [![Flux IR v0.1](https://img.shields.io/badge/Flux%20IR-v0.1-00CDFE)](./docs/flux-v0_1.md)
5
+ [![License: MIT](https://img.shields.io/github/license/cbassuarez/flux)](../../LICENSE)
6
+
7
+ This extension adds first-class editor support for the **Flux** score language — a grid-based notation and rule language for spatial music and graphic scores.
8
+
9
+ ## Features
10
+
11
+ - Syntax highlighting for `.flux` files:
12
+ - Core keywords (`document`, `state`, `grid`, `cell`, `rule`, `when`, `then`, `runtime`, etc.).
13
+ - Logical operators, event types, and the `neighbors.*` namespace.
14
+ - On-the-fly diagnostics powered by `@flux-lang/core`:
15
+ - Parse errors surfaced as editor diagnostics.
16
+ - Basic static checks (unknown `grid = ...`, invalid timers, unsupported `neighbors.*` methods).
17
+ - Plays nicely with the `flux` CLI:
18
+ - `flux parse` → canonical IR JSON.
19
+ - `flux check` → parse + static checks, same engine as the extension.
20
+
21
+ ## Flux IR
22
+
23
+ Flux has a well-defined JSON IR (intermediate representation) that mirrors the TypeScript `FluxDocument` AST.
24
+
25
+ - IR docs: `packages/core/docs/flux-v0_1.md` (or wherever you’ve put the spec).
26
+ - Programmatically: `parseDocument(source)` from `@flux-lang/core` + `JSON.stringify`.
27
+
28
+ ## Repository
29
+
30
+ This extension lives in the `flux` monorepo:
31
+
32
+ - GitHub: https://github.com/cbassuarez/flux
33
+ - Core library: `packages/core`
34
+ - CLI: `packages/cli`
35
+ - VS Code extension: `packages/vscode-flux`
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/extension.ts
31
+ var extension_exports = {};
32
+ __export(extension_exports, {
33
+ activate: () => activate,
34
+ deactivate: () => deactivate
35
+ });
36
+ module.exports = __toCommonJS(extension_exports);
37
+ var vscode = __toESM(require("vscode"));
38
+ var import_core = require("@flux-lang/core");
39
+ var DIAGNOSTIC_SOURCE_PARSE = "flux-parser";
40
+ var DIAGNOSTIC_SOURCE_CHECK = "flux-check";
41
+ var diagnosticCollection;
42
+ var debounceTimers = /* @__PURE__ */ new Map();
43
+ function activate(context) {
44
+ diagnosticCollection = vscode.languages.createDiagnosticCollection("flux");
45
+ context.subscriptions.push(diagnosticCollection);
46
+ for (const doc of vscode.workspace.textDocuments) {
47
+ if (doc.languageId === "flux") {
48
+ validateDocument(doc);
49
+ }
50
+ }
51
+ context.subscriptions.push(
52
+ vscode.workspace.onDidOpenTextDocument((doc) => {
53
+ if (doc.languageId === "flux") {
54
+ validateDocument(doc);
55
+ }
56
+ })
57
+ );
58
+ context.subscriptions.push(
59
+ vscode.workspace.onDidChangeTextDocument((event) => {
60
+ const doc = event.document;
61
+ if (doc.languageId !== "flux") return;
62
+ const key = doc.uri.toString();
63
+ const existing = debounceTimers.get(key);
64
+ if (existing) {
65
+ clearTimeout(existing);
66
+ }
67
+ const timer = setTimeout(() => {
68
+ debounceTimers.delete(key);
69
+ validateDocument(doc);
70
+ }, 400);
71
+ debounceTimers.set(key, timer);
72
+ })
73
+ );
74
+ context.subscriptions.push(
75
+ vscode.workspace.onDidSaveTextDocument((doc) => {
76
+ if (doc.languageId === "flux") {
77
+ validateDocument(doc);
78
+ }
79
+ })
80
+ );
81
+ context.subscriptions.push(
82
+ vscode.commands.registerCommand("flux.showIR", async () => {
83
+ const editor = vscode.window.activeTextEditor;
84
+ if (!editor) {
85
+ void vscode.window.showInformationMessage(
86
+ "No active editor."
87
+ );
88
+ return;
89
+ }
90
+ const doc = editor.document;
91
+ if (doc.languageId !== "flux") {
92
+ void vscode.window.showInformationMessage(
93
+ "Active document is not a Flux file."
94
+ );
95
+ return;
96
+ }
97
+ const text = doc.getText();
98
+ let ir;
99
+ try {
100
+ ir = (0, import_core.parseDocument)(text);
101
+ } catch (err) {
102
+ const msg = err?.message ?? "Unknown parse error.";
103
+ void vscode.window.showErrorMessage(
104
+ `Flux parse error: ${msg}`
105
+ );
106
+ return;
107
+ }
108
+ const json = JSON.stringify(ir, null, 2);
109
+ const irDoc = await vscode.workspace.openTextDocument({
110
+ language: "json",
111
+ content: json
112
+ });
113
+ await vscode.window.showTextDocument(irDoc, {
114
+ preview: true
115
+ });
116
+ })
117
+ );
118
+ }
119
+ function deactivate() {
120
+ if (diagnosticCollection) {
121
+ diagnosticCollection.dispose();
122
+ diagnosticCollection = void 0;
123
+ }
124
+ for (const timer of debounceTimers.values()) {
125
+ clearTimeout(timer);
126
+ }
127
+ debounceTimers.clear();
128
+ }
129
+ function validateDocument(doc) {
130
+ if (!diagnosticCollection) return;
131
+ if (doc.languageId !== "flux") return;
132
+ const diagnostics = [];
133
+ const text = doc.getText();
134
+ let parsed = null;
135
+ try {
136
+ parsed = (0, import_core.parseDocument)(text);
137
+ } catch (err) {
138
+ const diag = createParseDiagnostic(doc, err);
139
+ diagnostics.push(diag);
140
+ diagnosticCollection.set(doc.uri, diagnostics);
141
+ return;
142
+ }
143
+ try {
144
+ const messages = (0, import_core.checkDocument)(doc.uri.fsPath || "<memory>", parsed);
145
+ for (const message of messages) {
146
+ const diag = createCheckDiagnostic(doc, message);
147
+ diagnostics.push(diag);
148
+ }
149
+ } catch (err) {
150
+ const msg = err?.message ?? "Unknown static check failure.";
151
+ const diag = new vscode.Diagnostic(
152
+ new vscode.Range(0, 0, 0, 1),
153
+ `Static checks failed: ${msg}`,
154
+ vscode.DiagnosticSeverity.Warning
155
+ );
156
+ diag.source = DIAGNOSTIC_SOURCE_CHECK;
157
+ diagnostics.push(diag);
158
+ }
159
+ diagnosticCollection.set(doc.uri, diagnostics);
160
+ }
161
+ function createParseDiagnostic(doc, error) {
162
+ const message = error?.message ?? String(error);
163
+ const parseMatch = /Parse error at (\d+):(\d+) near '([^']*)': (.*)/.exec(message);
164
+ if (parseMatch) {
165
+ const [, lineStr, colStr, near, detail] = parseMatch;
166
+ const line = Math.max(0, Number(lineStr) - 1);
167
+ const col = Math.max(0, Number(colStr) - 1);
168
+ const range = safeRange(doc, line, col);
169
+ const diag2 = new vscode.Diagnostic(
170
+ range,
171
+ `Parse error near '${near}': ${detail}`,
172
+ vscode.DiagnosticSeverity.Error
173
+ );
174
+ diag2.source = DIAGNOSTIC_SOURCE_PARSE;
175
+ return diag2;
176
+ }
177
+ const diag = new vscode.Diagnostic(
178
+ new vscode.Range(0, 0, 0, 1),
179
+ message,
180
+ vscode.DiagnosticSeverity.Error
181
+ );
182
+ diag.source = DIAGNOSTIC_SOURCE_PARSE;
183
+ return diag;
184
+ }
185
+ function createCheckDiagnostic(doc, line) {
186
+ const match = /^.*?:(\d+):(\d+):\s*(.*)$/.exec(line);
187
+ if (!match) {
188
+ const diag2 = new vscode.Diagnostic(
189
+ new vscode.Range(0, 0, 0, 1),
190
+ line,
191
+ vscode.DiagnosticSeverity.Warning
192
+ );
193
+ diag2.source = DIAGNOSTIC_SOURCE_CHECK;
194
+ return diag2;
195
+ }
196
+ const [, lineStr, colStr, rest] = match;
197
+ const lineNum = Math.max(0, Number(lineStr) - 1);
198
+ const colNum = Math.max(0, Number(colStr) - 1);
199
+ const message = rest;
200
+ const range = safeRange(doc, lineNum, colNum);
201
+ const diag = new vscode.Diagnostic(
202
+ range,
203
+ message,
204
+ vscode.DiagnosticSeverity.Warning
205
+ );
206
+ diag.source = DIAGNOSTIC_SOURCE_CHECK;
207
+ return diag;
208
+ }
209
+ function safeRange(doc, line, col) {
210
+ const clampedLine = Math.min(
211
+ Math.max(line, 0),
212
+ Math.max(doc.lineCount - 1, 0)
213
+ );
214
+ const textLine = doc.lineAt(clampedLine);
215
+ const maxCol = textLine.text.length;
216
+ const clampedCol = Math.min(Math.max(col, 0), maxCol);
217
+ return new vscode.Range(
218
+ clampedLine,
219
+ clampedCol,
220
+ clampedLine,
221
+ clampedCol + 1
222
+ );
223
+ }
224
+ // Annotate the CommonJS export names for ESM import in node:
225
+ 0 && (module.exports = {
226
+ activate,
227
+ deactivate
228
+ });
@@ -0,0 +1,27 @@
1
+ // packages/vscode-flux/language-configuration.json
2
+ {
3
+ "comments": {
4
+ "lineComment": "//",
5
+ "blockComment": ["/*", "*/"]
6
+ },
7
+ "brackets": [
8
+ ["{", "}"],
9
+ ["[", "]"],
10
+ ["(", ")"]
11
+ ],
12
+ "autoClosingPairs": [
13
+ { "open": "{", "close": "}" },
14
+ { "open": "[", "close": "]" },
15
+ { "open": "(", "close": ")" },
16
+ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
17
+ { "open": "'", "close": "'", "notIn": ["string", "comment"] }
18
+ ],
19
+ "surroundingPairs": [
20
+ ["{", "}"],
21
+ ["[", "]"],
22
+ ["(", ")"],
23
+ ["\"", "\""],
24
+ ["'", "'"]
25
+ ]
26
+ }
27
+
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@flux-lang/vscode-flux",
3
+ "displayName": "Flux Language Support",
4
+ "publisher": "cbassuarez",
5
+ "version": "0.1.7",
6
+ "description": "Language support for the Flux score language (syntax + diagnostics).",
7
+ "license": "MIT",
8
+ "author": "Sebastian Suarez-Solis",
9
+ "engines": {
10
+ "vscode": "^1.85.0"
11
+ },
12
+ "categories": [
13
+ "Programming Languages"
14
+ ],
15
+ "activationEvents": [
16
+ "onLanguage:flux",
17
+ "onCommand:flux.showIR"
18
+ ],
19
+ "main": "./dist/extension.js",
20
+ "files": [
21
+ "dist",
22
+ "package.json",
23
+ "README.md",
24
+ "language-configuration.json",
25
+ "syntaxes"
26
+ ],
27
+ "contributes": {
28
+ "languages": [
29
+ {
30
+ "id": "flux",
31
+ "aliases": [
32
+ "Flux",
33
+ "flux"
34
+ ],
35
+ "extensions": [
36
+ ".flux"
37
+ ],
38
+ "configuration": "./language-configuration.json"
39
+ }
40
+ ],
41
+ "grammars": [
42
+ {
43
+ "language": "flux",
44
+ "scopeName": "source.flux",
45
+ "path": "./syntaxes/flux.tmLanguage.json"
46
+ }
47
+ ],
48
+ "commands": [
49
+ {
50
+ "command": "flux.showIR",
51
+ "title": "Flux: Show IR JSON for Current File",
52
+ "category": "Flux"
53
+ }
54
+ ],
55
+ "menus": {
56
+ "editor/context": [
57
+ {
58
+ "command": "flux.showIR",
59
+ "when": "resourceLangId == flux",
60
+ "group": "navigation"
61
+ }
62
+ ]
63
+ }
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "https://github.com/cbassuarez/flux.git"
68
+ },
69
+ "galleryBanner": {
70
+ "color": "#111111",
71
+ "theme": "dark"
72
+ },
73
+ "scripts": {
74
+ "typecheck": "tsc -p tsconfig.json --noEmit",
75
+ "build": "npm run typecheck && esbuild src/extension.ts --bundle --platform=node --format=cjs --external:vscode --external:@flux-lang/core --outfile=dist/extension.js",
76
+ "package": "vsce package",
77
+ "publish": "vsce publish",
78
+ "test": "echo \"No tests yet\""
79
+ },
80
+ "dependencies": {
81
+ "@flux-lang/core": "workspace:^"
82
+ },
83
+ "devDependencies": {
84
+ "@types/vscode": "^1.85.0",
85
+ "@types/node": "^22.0.0",
86
+ "esbuild": "^0.24.0",
87
+ "typescript": "^5.6.0"
88
+ }
89
+ }
@@ -0,0 +1,105 @@
1
+ {
2
+ "scopeName": "source.flux",
3
+ "name": "Flux",
4
+ "fileTypes": ["flux"],
5
+ "patterns": [
6
+ { "include": "#comments" },
7
+ { "include": "#strings" },
8
+ { "include": "#numbers" },
9
+ { "include": "#eventStrings" },
10
+ { "include": "#neighbors" },
11
+ { "include": "#keywords" },
12
+ { "include": "#logicalOperators" },
13
+ { "include": "#operators" }
14
+ ],
15
+ "repository": {
16
+ "comments": {
17
+ "patterns": [
18
+ {
19
+ "name": "comment.line.double-slash.flux",
20
+ "match": "//.*$"
21
+ },
22
+ {
23
+ "name": "comment.block.flux",
24
+ "begin": "/\\*",
25
+ "end": "\\*/"
26
+ }
27
+ ]
28
+ },
29
+ "strings": {
30
+ "patterns": [
31
+ {
32
+ "name": "string.quoted.double.flux",
33
+ "begin": "\"",
34
+ "end": "\"",
35
+ "patterns": [
36
+ {
37
+ "name": "constant.character.escape.flux",
38
+ "match": "\\\\."
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ "eventStrings": {
45
+ "patterns": [
46
+ {
47
+ "name": "string.quoted.double.flux constant.language.event-type.flux",
48
+ "match": "\"(click|hover|input)\""
49
+ }
50
+ ]
51
+ },
52
+ "numbers": {
53
+ "patterns": [
54
+ {
55
+ "name": "constant.numeric.flux",
56
+ "match": "\\b\\d+(?:\\.\\d+)?\\b"
57
+ }
58
+ ]
59
+ },
60
+ "neighbors": {
61
+ "patterns": [
62
+ {
63
+ "name": "support.class.neighbors.flux",
64
+ "match": "\\bneighbors\\b"
65
+ },
66
+ {
67
+ "name": "support.function.neighbors.flux",
68
+ "match": "(?<=neighbors\\.)\\b(all|orth)\\b"
69
+ }
70
+ ]
71
+ },
72
+ "keywords": {
73
+ "patterns": [
74
+ {
75
+ "name": "keyword.control.flux",
76
+ "match": "\\b(document|meta|state|runtime|grid|cell|rule|when|then|else)\\b"
77
+ },
78
+ {
79
+ "name": "keyword.other.flux",
80
+ "match": "\\b(mode|on|grid|topology|size|tags|content|dynamic|param|eventsApply|docstepAdvance|timer)\\b"
81
+ }
82
+ ]
83
+ },
84
+ "logicalOperators": {
85
+ "patterns": [
86
+ {
87
+ "name": "keyword.operator.logical.flux",
88
+ "match": "\\b(and|or|not)\\b"
89
+ },
90
+ {
91
+ "name": "keyword.operator.logical.flux",
92
+ "match": "&&|\\|\\||!"
93
+ }
94
+ ]
95
+ },
96
+ "operators": {
97
+ "patterns": [
98
+ {
99
+ "name": "keyword.operator.flux",
100
+ "match": "==|!=|===|!==|<=|>=|<|>|=|\\+|-|\\*|/"
101
+ }
102
+ ]
103
+ }
104
+ }
105
+ }