From dec08cc0d5de740ad23129c70c19bb5119fcf2db Mon Sep 17 00:00:00 2001 From: filipw Date: Thu, 9 Jul 2026 18:02:36 +0200 Subject: [PATCH] added sample extension and removed ord --- .gitignore | 3 ++ .vscode/launch.json | 15 ++++++ build.js | 11 ++++ grammars/qsharp.tmLanguage | 2 +- grammars/qsharp.tmLanguage.json | 2 +- src/qsharp.tmLanguage.yml | 2 +- test-extension/README.md | 38 +++++++++++++ test-extension/language-configuration.json | 22 ++++++++ test-extension/package.json | 28 ++++++++++ test-extension/sample.qs | 63 ++++++++++++++++++++++ 10 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 test-extension/README.md create mode 100644 test-extension/language-configuration.json create mode 100644 test-extension/package.json create mode 100644 test-extension/sample.qs diff --git a/.gitignore b/.gitignore index 9041a63..de604ba 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ out/ # macOS .DS_Store + +# generated copy for the grammar test extension (see build.js) +test-extension/qsharp.tmLanguage.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e40717f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Test grammar (Extension Development Host)", + "type": "extensionHost", + "request": "launch", + "args": [ + "--disable-extension=quantum.qsharp-lang-vscode", + "--extensionDevelopmentPath=${workspaceFolder}/test-extension", + "${workspaceFolder}/test-extension/sample.qs" + ] + } + ] +} diff --git a/build.js b/build.js index fa90067..d3194ac 100644 --- a/build.js +++ b/build.js @@ -31,6 +31,17 @@ function build() { ); console.log("Built grammars/qsharp.tmLanguage and grammars/qsharp.tmLanguage.json"); + + // Keep a copy inside the test extension (gitignored) so its manifest can + // reference a grammar within its own folder — VS Code warns otherwise. + const testExtDir = path.join(__dirname, "test-extension"); + if (fs.existsSync(testExtDir)) { + fs.writeFileSync( + path.join(testExtDir, "qsharp.tmLanguage.json"), + JSON.stringify(grammar, null, "\t") + "\n" + ); + console.log("Refreshed test-extension/qsharp.tmLanguage.json"); + } } build(); diff --git a/grammars/qsharp.tmLanguage b/grammars/qsharp.tmLanguage index 0395032..6787a0c 100644 --- a/grammars/qsharp.tmLanguage +++ b/grammars/qsharp.tmLanguage @@ -718,7 +718,7 @@ name support.type.constraint.qsharp match - \b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Exp)\b + \b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Iterable|Exp)\b name diff --git a/grammars/qsharp.tmLanguage.json b/grammars/qsharp.tmLanguage.json index 34b2b66..4ee5955 100644 --- a/grammars/qsharp.tmLanguage.json +++ b/grammars/qsharp.tmLanguage.json @@ -477,7 +477,7 @@ }, { "name": "support.type.constraint.qsharp", - "match": "\\b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Exp)\\b" + "match": "\\b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Iterable|Exp)\\b" }, { "name": "punctuation.squarebracket.open.qsharp", diff --git a/src/qsharp.tmLanguage.yml b/src/qsharp.tmLanguage.yml index f4e5b0d..c08a2aa 100644 --- a/src/qsharp.tmLanguage.yml +++ b/src/qsharp.tmLanguage.yml @@ -259,7 +259,7 @@ repository: - name: keyword.operator.qsharp match: \+ - name: support.type.constraint.qsharp - match: \b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Exp)\b + match: \b(Eq|Add|Sub|Mul|Div|Mod|Signed|Ord|Show|Integral|Iterable|Exp)\b - name: punctuation.squarebracket.open.qsharp match: \[ - name: punctuation.squarebracket.close.qsharp diff --git a/test-extension/README.md b/test-extension/README.md new file mode 100644 index 0000000..6e48da9 --- /dev/null +++ b/test-extension/README.md @@ -0,0 +1,38 @@ +# Grammar test extension + +A throwaway VS Code extension used to manually test the Q# TextMate grammar in a +real editor, isolated from the QDK extension (no language server, so no semantic +highlighting paints over the grammar). + +It contributes the `qsharp` language for `.qs` files. Its grammar +(`qsharp.tmLanguage.json`) is generated into this folder by `npm run build` +(gitignored) so the manifest can reference a file inside its own folder — VS Code +warns about grammar paths that escape the extension directory. + +## Usage + +1. Build the grammar so `grammars/qsharp.tmLanguage.json` is current: + + ```bash + npm run build + ``` + +2. Open the **repository root** in VS Code and press **F5** (the + `Test grammar (Extension Development Host)` launch config). This opens an + Extension Development Host with the official QDK extension disabled and + [`sample.qs`](./sample.qs) loaded. + +3. Verify tokenization with **`Developer: Inspect Editor Tokens and Scopes`** + (Command Palette) — hover a token to see its `source.qsharp` scopes. This is + the authoritative check; colors are just the active theme's scope mapping. + +4. After editing `src/qsharp.tmLanguage.yml`, re-run `npm run build` and reload + the host window (`Cmd/Ctrl+R`) to pick up the new grammar. + +## Notes + +- The launch config disables `quantum.qsharp-lang-vscode` only inside the + development host — your normal editor is untouched. +- If you prefer not to use F5, symlink this folder into your extensions dir + (`ln -s "$PWD/test-extension" ~/.vscode/extensions/qsharp-tmlanguage-test`), + disable the QDK extension, and reload. diff --git a/test-extension/language-configuration.json b/test-extension/language-configuration.json new file mode 100644 index 0000000..07c64f3 --- /dev/null +++ b/test-extension/language-configuration.json @@ -0,0 +1,22 @@ +{ + "comments": { + "lineComment": "//" + }, + "brackets": [ + ["[", "]"], + ["(", ")"], + ["{", "}"] + ], + "autoClosingPairs": [ + ["[", "]"], + ["(", ")"], + ["{", "}"], + { "open": "\"", "close": "\"", "notIn": ["string"] } + ], + "surroundingPairs": [ + ["[", "]"], + ["(", ")"], + ["{", "}"], + ["\"", "\""] + ] +} diff --git a/test-extension/package.json b/test-extension/package.json new file mode 100644 index 0000000..8732fe3 --- /dev/null +++ b/test-extension/package.json @@ -0,0 +1,28 @@ +{ + "name": "qsharp-tmlanguage-test-extension", + "displayName": "Q# TextMate grammar (dev)", + "description": "Loads the locally-built Q# grammar for manual testing in an Extension Development Host.", + "publisher": "local", + "version": "0.0.0", + "private": true, + "engines": { + "vscode": "^1.75.0" + }, + "contributes": { + "languages": [ + { + "id": "qsharp", + "aliases": ["Q#", "qsharp"], + "extensions": [".qs"], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "qsharp", + "scopeName": "source.qsharp", + "path": "./qsharp.tmLanguage.json" + } + ] + } +} diff --git a/test-extension/sample.qs b/test-extension/sample.qs new file mode 100644 index 0000000..d024852 --- /dev/null +++ b/test-extension/sample.qs @@ -0,0 +1,63 @@ +import Std.Diagnostics.*; +import Std.Math.PI as Pi; + +/// A doc comment describing a struct. +struct Complex { Re : Double, Im : Double } + +@EntryPoint() +operation Main() : Result[] { + // literals: decimal, hex, binary, octal, BigInt, float, separators + let ints = [42, 0xFF, 0b1010, 0o52, 1_000]; + let big = 42L; + let approx = 1.0e-3; + + // qubit allocation (single + tuple), gates, measurement, reset + use qs = Qubit[3]; + use (control, target) = (Qubit(), Qubit()); + for i in 0..Length(qs) - 1 { + H(qs[i]); + } + Controlled X([control], target); + Rx(Pi() / 4.0, target); + + // structs: construction, copy-and-update, field access + let c = new Complex { Re = 1.0, Im = 0.0 }; + let c2 = new Complex { ...c, Im = 2.0 }; + let magnitudeSquared = c2.Re * c2.Re + c2.Im * c2.Im; + + // operators: comparison, bitwise, ternary, ranges + let flags = (5 &&& 3) ||| (1 <<< 2); + let sign = magnitudeSquared > 0.0 ? One | Zero; + let slice = ints[1..2]; + + // lambdas, partial application, string interpolation + let addOne = x -> x + 1; + let applyX = q => X(q); + Message($"c2 = {c2.Re} + {c2.Im}i, sign = {sign}"); + + // control flow: if / elif / else, within-apply, mutation without `set` + mutable counter = 0; + counter += 1; + if counter == 1 { + Message("one"); + } elif counter == 2 { + Message("two"); + } else { + Message("many"); + } + + within { + H(qs[0]); + } apply { + Z(qs[0]); + } + + ResetAll(qs); + return MeasureEachZ(qs); +} + +function Sum2<'T : Add>(a : 'T, b : 'T) : 'T { + a + b +} + +export Main, Sum2, Complex;