├── .envrc ├── .env.template ├── syntax-tests ├── colon.nix ├── sample.nix ├── bug-508.nix ├── colon2.nix ├── colon.nix.snap ├── sample.nix.snap ├── bug-508.nix.snap ├── colon2.nix.snap └── misc.nix ├── commitlint.config.js ├── images ├── icon.png ├── docs │ ├── linting.png │ ├── md-embed-nix.png │ └── nix-syntax-highlight.png └── icon.svg ├── .vscode-test.js ├── src ├── global.d.ts ├── grammar │ ├── build.ts │ ├── helpers.ts │ ├── injection.ts │ └── nix.ts ├── extension.test.ts ├── utils.ts ├── extension.ts ├── configuration.ts ├── formatter.ts ├── process-runner.ts ├── linter.ts ├── client.ts └── .gitignore ├── .vscodeignore ├── shell.nix ├── .vscode ├── tasks.json └── launch.json ├── lefthook.yml ├── .github ├── workflows │ ├── test.yaml │ └── release-pr.yml └── dependabot.yml ├── snippets.json ├── install.md ├── biome.json ├── tsconfig.json ├── LICENSE ├── dist ├── injection.json └── nix.tmLanguage.json ├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── language-configuration.json ├── package.json └── CHANGELOG.md /.envrc: -------------------------------------------------------------------------------- 1 | use nix -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | OVS_PAT= 2 | -------------------------------------------------------------------------------- /syntax-tests/colon.nix: -------------------------------------------------------------------------------- 1 | [ { targets = [ "}:" ]; } ] 2 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nix-community/vscode-nix-ide/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/docs/linting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nix-community/vscode-nix-ide/HEAD/images/docs/linting.png -------------------------------------------------------------------------------- /images/docs/md-embed-nix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nix-community/vscode-nix-ide/HEAD/images/docs/md-embed-nix.png -------------------------------------------------------------------------------- /images/docs/nix-syntax-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nix-community/vscode-nix-ide/HEAD/images/docs/nix-syntax-highlight.png -------------------------------------------------------------------------------- /.vscode-test.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vscode/test-cli'); 2 | 3 | module.exports = defineConfig({ files: 'out/src/**/*.test.ts', mocha: { ui: 'bdd' } }); 4 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "vscode-variables" { 2 | export default function variables( 3 | string: string, 4 | recursive?: boolean, 5 | ): string; 6 | } 7 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | */** 4 | 5 | # Whitelist what you need 6 | !.vscodeignore 7 | !dist/* 8 | !images/* 9 | !language-configuration.json 10 | !LICENSE 11 | !snippets.json 12 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { } }: 2 | pkgs.mkShell { 3 | buildInputs = with pkgs; [ 4 | nodejs 5 | esbuild 6 | bun 7 | ]; 8 | shellHook = '' 9 | bun install 10 | ''; 11 | } 12 | -------------------------------------------------------------------------------- /syntax-tests/sample.nix: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "source.nix" "sample testcase" 2 | 3 | let 4 | a = abort "will never happen"; 5 | b = "hello"; 6 | c = "world"; 7 | # the following lines miss semi-colons on purpose 8 | path = ./relative/path 9 | sp_path = ./relative/${path} 10 | in b + c -------------------------------------------------------------------------------- /syntax-tests/bug-508.nix: -------------------------------------------------------------------------------- 1 | # from https://github.com/nix-community/vscode-nix-ide/issues/508 2 | { 3 | case_1 = (if 2 > 1 then "a" else "b"); 4 | case_2 = (if 2 < 1 then "a" else "b"); 5 | case_3 = (if 2 >= 1 then "a" else "b"); 6 | case_4 = (if 2 <= 1 then "a" else "b"); 7 | case_5 = (true -> true); # (implication operator) 8 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Bun Debug Build", 6 | "type": "shell", 7 | "command": "bun run build --sourcemap", 8 | "options": { 9 | "cwd": "${workspaceFolder}" 10 | }, 11 | "group": "build", 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | # EXAMPLE USAGE: 2 | # 3 | # Refer for explanation to following link: 4 | # https://evilmartians.github.io/lefthook/configuration/ 5 | 6 | pre-commit: 7 | parallel: true 8 | fail_on_change: always 9 | jobs: 10 | - run: bun run pretest 11 | glob: "*.{js,ts,jsx,tsx}" 12 | 13 | commit-msg: 14 | fail_on_change: always 15 | jobs: 16 | - run: bun run commitlint --edit 17 | -------------------------------------------------------------------------------- /src/grammar/build.ts: -------------------------------------------------------------------------------- 1 | /// Generate json files from the grammar definitions. 2 | 3 | import * as helpers from "./helpers"; 4 | import { nixInjectionGrammar } from "./injection"; 5 | import { source_nix } from "./nix"; 6 | 7 | // This code only runs when 'bun run index.ts' is executed directly. 8 | helpers.toJson("dist/injection.json", nixInjectionGrammar); 9 | helpers.toJson("dist/nix.tmLanguage.json", source_nix); 10 | -------------------------------------------------------------------------------- /src/extension.test.ts: -------------------------------------------------------------------------------- 1 | import assert = require("node:assert"); 2 | 3 | import * as vscode from "vscode"; 4 | 5 | describe("The resources", () => { 6 | const extension = vscode.extensions.getExtension("jnoortheen.vscode-nix-ide"); 7 | if (!extension) { 8 | assert.fail("Extension not found"); 9 | } 10 | // const iconName = "images/icon.png"; 11 | 12 | // it("provides valid icon paths", async () => { 13 | 14 | // }); 15 | }); 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [master, main] 6 | pull_request: 7 | branches: [master, main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | 17 | steps: 18 | - uses: actions/checkout@v5 19 | - uses: oven-sh/setup-bun@v2 20 | - run: bun install 21 | - run: bun run pretest 22 | -------------------------------------------------------------------------------- /snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "conditional": { 3 | "prefix": ["if"], 4 | "body": ["if $1 then $2 else $0"], 5 | "description": "Conditional expression" 6 | }, 7 | "let": { 8 | "prefix": ["let"], 9 | "body": ["let $1;", "in $0"], 10 | "description": "Let expression" 11 | }, 12 | "rec": { 13 | "prefix": ["rec"], 14 | "body": ["rec { $1", "\t$0}"], 15 | "description": "Recursive Set" 16 | }, 17 | "with": { 18 | "prefix": ["with"], 19 | "body": ["with $1;$0"], 20 | "description": "With expression" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /install.md: -------------------------------------------------------------------------------- 1 | ## Installation 🔨 2 | 3 | Available on both the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=jnoortheen.nix-ide) and the [Open VSX Registry](https://open-vsx.org/extension/jnoortheen/nix-ide). 4 | 5 | You can also open the Command Palette (Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on macOS) and enter `ext install jnoortheen.nix-ide` to install the extension, or download it from the [latest release](https://github.com/nix-community/vscode-nix-ide/releases/latest). 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates for npm 4 | - package-ecosystem: "npm" 5 | # Look for `package.json` and `lock` files in the `root` directory 6 | directory: "/" 7 | # Check the npm registry for updates every week (weekdays) 8 | schedule: 9 | interval: "monthly" 10 | groups: 11 | production-dependencies: 12 | dependency-type: 'production' 13 | development-dependencies: 14 | dependency-type: 'development' 15 | 16 | - package-ecosystem: 'github-actions' 17 | directory: '/' 18 | schedule: 19 | interval: 'monthly' -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.2.6/schema.json", 3 | "vcs": { 4 | "enabled": true, 5 | "clientKind": "git", 6 | "useIgnoreFile": true 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "includes": ["**"] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "space" 15 | }, 16 | "assist": { "actions": { "source": { "organizeImports": "on" } } }, 17 | "linter": { 18 | "enabled": true, 19 | "rules": { 20 | "recommended": true 21 | } 22 | }, 23 | "javascript": { 24 | "formatter": { 25 | "quoteStyle": "double" 26 | }, 27 | "globals": ["exports"] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /syntax-tests/colon2.nix: -------------------------------------------------------------------------------- 1 | { 2 | nps.stacks.monitoring.prometheus.config = lib.mkIf cfg.enablePrometheusExport { 3 | scrape_configs = [ 4 | { 5 | job_name = "blocky"; 6 | honor_timestamps = true; 7 | metrics_path = "/metrics"; 8 | scheme = "http"; 9 | static_configs = [ { targets = [ "${name}:6000" ]; } ]; 10 | shell = /* sh */ '' 11 | echo "Starting Prometheus scrape for Blocky at ${name}:6000" 12 | ''; 13 | } 14 | ]; 15 | }; 16 | nps.stacks.${name} = { 17 | settings = { 18 | prometheus = { 19 | enabled = cfg.enablePrometheusExport; 20 | level = "aggregated"; 21 | listen_address = "0.0.0.0"; 22 | listen_port = 6060; 23 | }; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Launch with Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "preLaunchTask": "Bun Debug Build", 14 | "args": [ 15 | "--extensionDevelopmentPath=${workspaceFolder}" 16 | ], 17 | "outFiles": [ 18 | "${workspaceRoot}/dist/**/*.js" 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2018", 5 | "outDir": "dist", 6 | "lib": [ 7 | "es2018" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | /* Strict Type-Checking Option */ 12 | "strict": true /* enable all strict type-checking options */, 13 | /* Additional Checks */ 14 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 15 | "noUnusedLocals": true /* Report errors on unused locals. */ 16 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 17 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test" 22 | ] 23 | } -------------------------------------------------------------------------------- /images/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { LSPObject } from "vscode-languageclient"; 2 | import variables from "vscode-variables"; 3 | 4 | /** 5 | * transform config value by vscode variables 6 | * @link https://www.npmjs.com/package/vscode-variables?activeTab=readme 7 | */ 8 | export const transformConfigValueByVscodeVariables = < 9 | T extends string | boolean | LSPObject, 10 | >( 11 | _cfg: T, 12 | ): T => { 13 | let cfg = _cfg; 14 | try { 15 | if (typeof cfg === "string") { 16 | cfg = variables(cfg) as T; 17 | } else if (!!cfg && typeof cfg === "object") { 18 | for (const key of Object.keys(cfg)) { 19 | cfg[key] = transformConfigValueByVscodeVariables(cfg[key]); 20 | } 21 | } else if (Array.isArray(cfg) && cfg.length > 0) { 22 | cfg = cfg.map( 23 | (item) => transformConfigValueByVscodeVariables(item) as unknown, 24 | ) as T; 25 | } 26 | return cfg; 27 | } catch (err) { 28 | console.error(err); 29 | return cfg; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 noor 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. -------------------------------------------------------------------------------- /syntax-tests/colon.nix.snap: -------------------------------------------------------------------------------- 1 | >[ { targets = [ "}:" ]; } ] 2 | #^ source.nix punctuation.definition.list.nix 3 | # ^ source.nix 4 | # ^ source.nix punctuation.definition.attrset.nix 5 | # ^ source.nix 6 | # ^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 7 | # ^ source.nix 8 | # ^ source.nix keyword.operator.bind.nix 9 | # ^ source.nix 10 | # ^ source.nix punctuation.definition.list.nix 11 | # ^ source.nix 12 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 13 | # ^^ source.nix string.quoted.double.nix 14 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 15 | # ^ source.nix 16 | # ^ source.nix punctuation.definition.list.nix 17 | # ^ source.nix punctuation.terminator.bind.nix 18 | # ^ source.nix 19 | # ^ source.nix punctuation.definition.attrset.nix 20 | # ^ source.nix 21 | # ^ source.nix punctuation.definition.list.nix 22 | > -------------------------------------------------------------------------------- /dist/injection.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileTypes": [], 3 | "injectionSelector": "L:text.html.markdown", 4 | "patterns": [ 5 | { 6 | "include": "#nixCodeBlock" 7 | } 8 | ], 9 | "scopeName": "markdown.nix.codeblock", 10 | "repository": { 11 | "nixCodeBlock": { 12 | "begin": "(^|\\G)(\\s*)(\\`{3,}|~{3,})\\s*(?i:(nix)(\\s+[^`~]*)?$)", 13 | "name": "markup.fenced_code.block.markdown", 14 | "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", 15 | "beginCaptures": { 16 | "3": { 17 | "name": "punctuation.definition.markdown" 18 | }, 19 | "5": { 20 | "name": "fenced_code.block.language" 21 | }, 22 | "6": { 23 | "name": "fenced_code.block.language.attributes" 24 | } 25 | }, 26 | "endCaptures": { 27 | "3": { 28 | "name": "punctuation.definition.markdown" 29 | } 30 | }, 31 | "patterns": [ 32 | { 33 | "begin": "(^|\\G)(\\s*)(.*)", 34 | "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", 35 | "contentName": "meta.embedded.block.nix", 36 | "patterns": [ 37 | { 38 | "include": "source.nix" 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/grammar/helpers.ts: -------------------------------------------------------------------------------- 1 | // biome-ignore-all lint/suspicious/noExplicitAny: false positive 2 | import * as tm from "tmlanguage-generator"; 3 | 4 | export const bounded = (text: string) => `\\b${text}\\b`; 5 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 6 | export const after = (regex: string) => `(?<=${regex})`; 7 | export const notAfter = (regex: string) => `(? `(?=${regex})`; 9 | export const notBefore = (regex: string) => `(?!${regex})`; 10 | 11 | function isGrammar(obj: any): obj is tm.Grammar { 12 | return obj && typeof obj === "object" && "$schema" in obj; 13 | } 14 | 15 | type Grammar = Map | tm.Grammar; 16 | 17 | export async function toJson(outputFile: string, grammar: Grammar) { 18 | try { 19 | // Convert the grammar object to a formatted JSON string (2-space indentation) 20 | const jsonString = isGrammar(grammar) 21 | ? await tm.emitJSON(grammar) 22 | : JSON.stringify(grammar, null, 2); 23 | // Use Bun.write to save the string to a file 24 | await Bun.write(outputFile, jsonString); 25 | } catch (error) { 26 | console.error("❌ Error converting or writing file:", error); 27 | } 28 | console.log(`✅ Success! Object successfully saved to ${outputFile}`); 29 | } 30 | -------------------------------------------------------------------------------- /src/grammar/injection.ts: -------------------------------------------------------------------------------- 1 | export const nixInjectionGrammar = { 2 | fileTypes: [], 3 | injectionSelector: "L:text.html.markdown", 4 | patterns: [ 5 | { 6 | include: "#nix-code-block", 7 | }, 8 | ], 9 | repository: { 10 | "nix-code-block": { 11 | begin: "(^|\\G)(\\s*)(\\`{3,}|~{3,})\\s*(?i:(nix)(\\s+[^`~]*)?$)", 12 | name: "markup.fenced_code.block.markdown", 13 | end: "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", 14 | beginCaptures: { 15 | "3": { 16 | name: "punctuation.definition.markdown", 17 | }, 18 | "5": { 19 | name: "fenced_code.block.language", 20 | }, 21 | "6": { 22 | name: "fenced_code.block.language.attributes", 23 | }, 24 | }, 25 | endCaptures: { 26 | "3": { 27 | name: "punctuation.definition.markdown", 28 | }, 29 | }, 30 | patterns: [ 31 | { 32 | begin: "(^|\\G)(\\s*)(.*)", 33 | while: "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", 34 | contentName: "meta.embedded.block.nix", 35 | patterns: [ 36 | { 37 | include: "source.nix", 38 | }, 39 | ], 40 | }, 41 | ], 42 | }, 43 | }, 44 | scopeName: "markdown.nix.codeblock", 45 | }; 46 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import type { ExtensionContext } from "vscode"; 2 | import * as vscode from "vscode"; 3 | import * as client from "./client"; 4 | import { config } from "./configuration"; 5 | import { formattingProviders } from "./formatter"; 6 | import { startLinting } from "./linter"; 7 | 8 | /** 9 | * Activate this extension. 10 | * 11 | * If LSP is enabled 12 | * then support IDE features with {@link https://github.com/oxalica/nil|nil} 13 | * Else 14 | * Format with nixpkgs-format 15 | * validate with nix-instantiate 16 | * 17 | * @param context The context for this extension 18 | * @return A promise for the initialization 19 | */ 20 | export async function activate(context: ExtensionContext): Promise { 21 | if (config.LSPEnabled) { 22 | try { 23 | await client.activate(context); 24 | } catch (err) { 25 | console.error(err); 26 | } 27 | } else { 28 | await startLinting(context); 29 | const subs = [ 30 | vscode.languages.registerDocumentFormattingEditProvider, 31 | vscode.languages.registerDocumentRangeFormattingEditProvider, 32 | ].map((func) => func("nix", formattingProviders)); 33 | context.subscriptions.concat(subs); 34 | } 35 | 36 | context.subscriptions.push( 37 | vscode.commands.registerCommand( 38 | "nix-ide.restartLanguageServer", 39 | async () => { 40 | if (config.LSPEnabled) { 41 | await client.restart(context); 42 | } 43 | }, 44 | ), 45 | ); 46 | 47 | vscode.workspace.onDidChangeConfiguration(async (event) => { 48 | if (config.requiresServerRestart(event)) { 49 | const choice = await vscode.window.showWarningMessage( 50 | "Configuration change requires restarting the language server", 51 | "Restart", 52 | ); 53 | if (choice === "Restart") { 54 | await client.restart(context); 55 | } 56 | } 57 | }); 58 | } 59 | 60 | export async function deactivate(): Promise { 61 | await client.deactivate(); 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/release-pr.yml: -------------------------------------------------------------------------------- 1 | name: Release PR 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | version-bump: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/checkout@v5 17 | with: 18 | fetch-depth: 0 19 | - name: Configure Git 20 | run: | 21 | git config --global user.name 'GitHub Actions' 22 | git config --global user.email 'actions@github.com' 23 | - name: Check Commit Message 24 | id: check_commit 25 | run: | 26 | commit_message=$(git log -1 --pretty=%B) 27 | if [[ "$commit_message" =~ ^chore\(release\):\ ([.0-9]+).* ]]; then 28 | version="${BASH_REMATCH[1]}" 29 | echo "is_release=true" >> $GITHUB_OUTPUT 30 | echo "version=$version" >> $GITHUB_OUTPUT 31 | else 32 | echo "is_release=false" >> $GITHUB_OUTPUT 33 | fi 34 | - uses: oven-sh/setup-bun@v2 35 | - name: generate changelog and bump version 36 | if: steps.check_commit.outputs.is_release == 'false' 37 | run: bunx standard-version 38 | - name: Create Pull Request 39 | if: steps.check_commit.outputs.is_release == 'false' 40 | uses: peter-evans/create-pull-request@v7 41 | with: 42 | title: "Pending Release" 43 | body: "Automated version bump using standard-version" 44 | branch: version-bump 45 | delete-branch: true 46 | base: main 47 | - name: tag current package version and push 48 | if: steps.check_commit.outputs.is_release == 'true' 49 | run: | 50 | git tag v${{ steps.check_commit.outputs.version }} || true 51 | git push --tags || true 52 | bun install --frozen-lockfile 53 | bun run package 54 | bun run ovsx publish *.vsix --pat ${{ secrets.OPEN_VSX_TOKEN }} || true 55 | bun run vsce publish -p ${{ secrets.VS_MARKETPLACE_TOKEN }} || true 56 | gh release create v${{ steps.check_commit.outputs.version }} nix-ide*.vsix --title "v${{ steps.check_commit.outputs.version }}" --notes "Release v${{ steps.check_commit.outputs.version }}" 57 | env: 58 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | -------------------------------------------------------------------------------- /src/configuration.ts: -------------------------------------------------------------------------------- 1 | import type { MessageItem, Uri } from "vscode"; 2 | import { 3 | type ConfigurationChangeEvent, 4 | type WorkspaceConfiguration, 5 | workspace, 6 | } from "vscode"; 7 | import type { LSPObject } from "vscode-languageclient"; 8 | import { transformConfigValueByVscodeVariables } from "./utils"; 9 | 10 | export interface UriMessageItem extends MessageItem { 11 | uri: Uri; 12 | } 13 | 14 | export class Config { 15 | readonly rootSection = "nix"; 16 | 17 | private get cfg(): WorkspaceConfiguration { 18 | return workspace.getConfiguration(this.rootSection); 19 | } 20 | 21 | private get( 22 | path: string, 23 | def_val: T, 24 | ): T { 25 | return transformConfigValueByVscodeVariables( 26 | this.cfg.get(path) ?? def_val, 27 | ); 28 | } 29 | 30 | get formatterPath(): Array { 31 | const path: Array | string = this.get("formatterPath", "nixfmt"); 32 | if (typeof path === "string") { 33 | switch (path) { 34 | case "nix3-fmt": 35 | return ["nix", "fmt", "--", "--"]; 36 | case "treefmt": 37 | return ["treefmt", "--stdin", "{file}"]; 38 | default: 39 | return [path]; 40 | } 41 | } 42 | return path; 43 | } 44 | 45 | get serverPath(): Array { 46 | const path: Array | string = this.get("serverPath", "nil"); 47 | if (typeof path === "string") { 48 | return [path]; 49 | } 50 | return path; 51 | } 52 | 53 | get LSPEnabled(): boolean { 54 | return this.get("enableLanguageServer", false); 55 | } 56 | 57 | get hiddenErrorKinds(): string[] { 58 | return this.get("hiddenLanguageServerErrors", []); 59 | } 60 | 61 | get serverSettings(): LSPObject { 62 | return this.get("serverSettings", {}); 63 | } 64 | 65 | requiresServerRestart(change: ConfigurationChangeEvent): boolean { 66 | // NOTE: this might be easier if all the settings were nested under 67 | // e.g. `"nix.languageServer" or something like that, to deduplicate keys 68 | return ( 69 | change.affectsConfiguration("nix.serverPath") || 70 | change.affectsConfiguration("nix.enableLanguageServer") 71 | ); 72 | } 73 | } 74 | export const config = new Config(); 75 | -------------------------------------------------------------------------------- /src/formatter.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { 3 | type DocumentFormattingEditProvider, 4 | type DocumentRangeFormattingEditProvider, 5 | Range, 6 | type TextDocument, 7 | TextEdit, 8 | } from "vscode"; 9 | import { config } from "./configuration"; 10 | import { type IProcessResult, runInWorkspace } from "./process-runner"; 11 | 12 | const FORMATTER: Array = config.formatterPath; 13 | 14 | /** 15 | * Get text edits to format a range in a document. 16 | * 17 | * @param document The document whose text to format 18 | * @param range The range within the document to format 19 | * @return A promise with the list of edits 20 | */ 21 | const getFormatRangeEdits = async ( 22 | document: TextDocument, 23 | range?: Range, 24 | ): Promise> => { 25 | const actualRange = document.validateRange( 26 | range || new Range(0, 0, Number.MAX_VALUE, Number.MAX_VALUE), 27 | ); 28 | let result: IProcessResult; 29 | try { 30 | FORMATTER.forEach((elm, i) => { 31 | FORMATTER[i] = elm.replace("{file}", document.fileName); 32 | }); 33 | result = await runInWorkspace( 34 | vscode.workspace.getWorkspaceFolder(document.uri), 35 | FORMATTER, 36 | document.getText(actualRange), 37 | ); 38 | } catch (error) { 39 | if (error instanceof Error) { 40 | await vscode.window.showErrorMessage( 41 | `Failed to run ${FORMATTER.join(" ")}: ${error.message}`, 42 | ); 43 | } 44 | // Re-throw the error to make the promise fail 45 | throw error; 46 | } 47 | return result.exitCode === 0 48 | ? [TextEdit.replace(actualRange, result.stdout)] 49 | : []; 50 | }; 51 | 52 | /** 53 | * A type for all formatting providers. 54 | */ 55 | type FormattingProviders = DocumentFormattingEditProvider & 56 | DocumentRangeFormattingEditProvider; 57 | 58 | /** 59 | * Formatting providers 60 | */ 61 | export const formattingProviders: FormattingProviders = { 62 | provideDocumentFormattingEdits: (document, _, token) => 63 | getFormatRangeEdits(document).then((edits) => 64 | token.isCancellationRequested 65 | ? [] 66 | : // tslint:disable-next-line:readonly-array 67 | (edits as TextEdit[]), 68 | ), 69 | provideDocumentRangeFormattingEdits: (document, range, _, token) => 70 | getFormatRangeEdits(document, range).then((edits) => 71 | token.isCancellationRequested 72 | ? [] 73 | : // tslint:disable-next-line:readonly-array 74 | (edits as TextEdit[]), 75 | ), 76 | }; 77 | -------------------------------------------------------------------------------- /src/process-runner.ts: -------------------------------------------------------------------------------- 1 | import { execFile } from "node:child_process"; 2 | import type { WorkspaceFolder } from "vscode"; 3 | 4 | /** 5 | * A system error, i.e. an error that results from a syscall. 6 | */ 7 | interface ISystemError extends Error { 8 | readonly errno: string; 9 | } 10 | 11 | /** 12 | * Whether an error is a system error. 13 | * 14 | * @param error The error to check 15 | */ 16 | const isSystemError = (error: Error): error is ISystemError => 17 | (error as ISystemError).errno !== undefined && 18 | typeof (error as ISystemError).errno === "string"; 19 | 20 | /** 21 | * A process error. 22 | * 23 | * A process error occurs when the process exited with a non-zero exit code. 24 | */ 25 | interface IProcessError extends Error { 26 | /** 27 | * The exit code of the process. 28 | */ 29 | readonly code: number; 30 | } 31 | 32 | /** 33 | * Whether an error is a process error. 34 | */ 35 | const isProcessError = (error: Error): error is IProcessError => 36 | !isSystemError(error) && 37 | (error as IProcessError).code !== undefined && 38 | (error as IProcessError).code > 0; 39 | 40 | /** 41 | * The result of a process. 42 | */ 43 | export interface IProcessResult { 44 | /** 45 | * The integral exit code. 46 | */ 47 | readonly exitCode: number; 48 | /** 49 | * The standard output. 50 | */ 51 | readonly stdout: string; 52 | /** 53 | * The standard error. 54 | */ 55 | readonly stderr: string; 56 | } 57 | 58 | /** 59 | * Run a command in a given workspace folder. 60 | * 61 | * If the workspace folder is undefined run the command in the working directory 62 | * if the vscode instance. 63 | * 64 | * @param folder The folder to run the command in 65 | * @param command The command array 66 | * @param stdin An optional string to feed to standard input 67 | * @return The result of the process as promise 68 | */ 69 | export const runInWorkspace = ( 70 | folder: WorkspaceFolder | undefined, 71 | command: ReadonlyArray, 72 | stdin?: string, 73 | ): Promise => 74 | new Promise((resolve, reject) => { 75 | const cwd = folder ? folder.uri.fsPath : process.cwd(); 76 | const child = execFile( 77 | command[0], 78 | command.slice(1), 79 | { cwd }, 80 | (error, stdout, stderr) => { 81 | if (error && !isProcessError(error)) { 82 | // Throw system errors, but do not fail if the command 83 | // fails with a non-zero exit code. 84 | console.error("Command error", command, error); 85 | reject(error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors 86 | } else { 87 | const exitCode = error ? error.code : 0; 88 | resolve({ stdout, stderr, exitCode }); 89 | } 90 | }, 91 | ); 92 | if (stdin && child.stdin) { 93 | child.stdin.end(stdin); 94 | } 95 | }); 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | !.vscode/settings.json 3 | !.vscode/tasks.json 4 | !.vscode/launch.json 5 | !.vscode/extensions.json 6 | *.code-workspace 7 | 8 | # Local History for Visual Studio Code 9 | .history/ 10 | 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | lerna-debug.log* 18 | 19 | # Diagnostic reports (https://nodejs.org/api/report.html) 20 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 21 | 22 | # Runtime data 23 | pids 24 | *.pid 25 | *.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | *.lcov 34 | 35 | # nyc test coverage 36 | .nyc_output 37 | 38 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 39 | .grunt 40 | 41 | # Bower dependency directory (https://bower.io/) 42 | bower_components 43 | 44 | # node-waf configuration 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | build/Release 49 | 50 | # Dependency directories 51 | node_modules/ 52 | jspm_packages/ 53 | 54 | # Snowpack dependency directory (https://snowpack.dev/) 55 | web_modules/ 56 | 57 | # TypeScript cache 58 | *.tsbuildinfo 59 | 60 | # Optional npm cache directory 61 | .npm 62 | 63 | # Optional eslint cache 64 | .eslintcache 65 | 66 | # Microbundle cache 67 | .rpt2_cache/ 68 | .rts2_cache_cjs/ 69 | .rts2_cache_es/ 70 | .rts2_cache_umd/ 71 | 72 | # Optional REPL history 73 | .node_repl_history 74 | 75 | # Output of 'npm pack' 76 | *.tgz 77 | 78 | # Yarn Integrity file 79 | .yarn-integrity 80 | 81 | # dotenv environment variables file 82 | .env 83 | .env.test 84 | 85 | # parcel-bundler cache (https://parceljs.org/) 86 | .cache 87 | .parcel-cache 88 | 89 | # Next.js build output 90 | .next 91 | out 92 | 93 | # Nuxt.js build / generate output 94 | .nuxt 95 | dist 96 | 97 | # Gatsby files 98 | .cache/ 99 | # Comment in the public line in if your project uses Gatsby and not Next.js 100 | # https://nextjs.org/blog/next-9-1#public-directory-support 101 | # public 102 | 103 | # vuepress build output 104 | .vuepress/dist 105 | 106 | # Serverless directories 107 | .serverless/ 108 | 109 | # FuseBox cache 110 | .fusebox/ 111 | 112 | # DynamoDB Local files 113 | .dynamodb/ 114 | 115 | # TernJS port file 116 | .tern-port 117 | 118 | # Stores VSCode versions used for testing VSCode extensions 119 | .vscode-test 120 | 121 | # yarn v2 122 | .yarn/cache 123 | .yarn/unplugged 124 | .yarn/build-state.yml 125 | .yarn/install-state.gz 126 | .pnp.* 127 | 128 | .fake 129 | .ionide 130 | 131 | # latest paket versions 132 | .paket/ 133 | paket-files/ 134 | 135 | *.vsix 136 | 137 | # some misc. proj. files 138 | .local/ 139 | .DS_Store 140 | .direnv/ 141 | .husky/ 142 | .idea/ 143 | 144 | # tree-sitter-nix build artifacts 145 | target/ 146 | tree-sitter-nix/ 147 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - Document the purpose of functions and classes. 4 | - Please mention new features in the `README.md` features section. Use screenshots when applicable. 5 | - The [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) style should be used for commit messages as it is used to generate the changelog. 6 | 7 | ## Development 8 | 9 | There is [direnv](https://direnv.net/) and [nix-shell](https://nixos.wiki/wiki/Development_environment_with_nix-shell) support so a dev environment can be created with the `nix-shell` command or a one-time `direnv allow` at the root of the repo. 10 | 11 | Press `F5` in VSCode to run an Extension Development Host instance with the extension installed. 12 | 13 | TypeScript is used to develop the extension. 14 | 15 | ```sh 16 | bun install # install dependencies 17 | bun run build # build the extension 18 | ``` 19 | 20 | ## Releasing a new version 21 | 22 | Complete `.env` with environment variables based on `.env.template`, 23 | 24 | ```sh 25 | # this will generate changelog and will create a GitHub release. This will also trigger jobs to publish the extension. 26 | bun run release 27 | 28 | # to manually publish the extension 29 | bun run publish 30 | ``` 31 | 32 | # VS Code Extension Quickstart 33 | 34 | ## What's in the folder 35 | 36 | * This folder contains all of the files necessary for your extension. 37 | * `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension. 38 | * `syntaxes/nix.YAML-tmLanguage` - this is the Text mate grammar file that is used for tokenization. This will get compiled to `syntaxes/nix.tmLanguage.json` during build. 39 | * `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets. 40 | 41 | ## Get up and running straight away 42 | 43 | * Make sure the language configuration settings in `language-configuration.json` are accurate. 44 | * Press `F5` to open a new window with your extension loaded. 45 | * Create a new file with a file name suffix matching your language. 46 | * Verify that syntax highlighting works and that the language configuration settings are working. 47 | 48 | ## Make changes 49 | 50 | * You can relaunch the extension from the debug toolbar after making changes to the files listed above. 51 | * You can also reload ( `Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 52 | 53 | ## Add more language features 54 | 55 | * To add features such as intellisense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs 56 | 57 | ## Install your extension 58 | 59 | * To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. 60 | * To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. 61 | -------------------------------------------------------------------------------- /syntax-tests/sample.nix.snap: -------------------------------------------------------------------------------- 1 | ># SYNTAX TEST "source.nix" "sample testcase" 2 | #^ source.nix comment.line.number-sign.nix 3 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.nix comment.line.number-sign.nix 4 | > 5 | >let 6 | #^^^ source.nix keyword.other.nix 7 | > a = abort "will never happen"; 8 | #^^ source.nix 9 | # ^ source.nix entity.other.attribute-name.multipart.nix 10 | # ^ source.nix 11 | # ^ source.nix keyword.operator.bind.nix 12 | # ^ source.nix 13 | # ^^^^^ source.nix support.function.nix 14 | # ^ source.nix 15 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 16 | # ^^^^^^^^^^^^^^^^^ source.nix string.quoted.double.nix 17 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 18 | # ^ source.nix punctuation.terminator.bind.nix 19 | > b = "hello"; 20 | #^^ source.nix 21 | # ^ source.nix entity.other.attribute-name.multipart.nix 22 | # ^ source.nix 23 | # ^ source.nix keyword.operator.bind.nix 24 | # ^ source.nix 25 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 26 | # ^^^^^ source.nix string.quoted.double.nix 27 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 28 | # ^ source.nix punctuation.terminator.bind.nix 29 | > c = "world"; 30 | #^^ source.nix 31 | # ^ source.nix entity.other.attribute-name.multipart.nix 32 | # ^ source.nix 33 | # ^ source.nix keyword.operator.bind.nix 34 | # ^ source.nix 35 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 36 | # ^^^^^ source.nix string.quoted.double.nix 37 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 38 | # ^ source.nix punctuation.terminator.bind.nix 39 | > # the following lines miss semi-colons on purpose 40 | #^^ source.nix 41 | # ^ source.nix comment.line.number-sign.nix 42 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.nix comment.line.number-sign.nix 43 | > path = ./relative/path 44 | #^^ source.nix 45 | # ^^^^ source.nix entity.other.attribute-name.multipart.nix 46 | # ^ source.nix 47 | # ^ source.nix keyword.operator.bind.nix 48 | # ^ source.nix 49 | # ^^^^^^^^^^^^^^^ source.nix string.unquoted.path.nix 50 | > sp_path = ./relative/${path} 51 | #^^ source.nix 52 | # ^^^^^^^ source.nix variable.parameter.name.nix 53 | # ^ source.nix 54 | # ^ source.nix invalid.illegal 55 | # ^ source.nix 56 | # ^^^^^^^^^^ source.nix string.unquoted.path.nix 57 | # ^ source.nix keyword.operator.nix 58 | # ^^ source.nix meta.embedded punctuation.section.embedded.begin.nix 59 | # ^^^^ source.nix meta.embedded variable.parameter.name.nix 60 | # ^ source.nix meta.embedded punctuation.section.embedded.end.nix 61 | >in b + c 62 | #^^ source.nix invalid.illegal.reserved.nix 63 | # ^ source.nix 64 | # ^ source.nix variable.parameter.name.nix 65 | # ^ source.nix 66 | # ^ source.nix keyword.operator.nix 67 | # ^ source.nix 68 | # ^ source.nix variable.parameter.name.nix -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nix IDE ✨💡🌟 2 | 3 | Adds [Nix](https://nixos.org/) language support for [Visual Studio Code](https://code.visualstudio.com/). 4 | 5 | ## Quickstart 🚀 6 | 7 | 1. [Install](./install.md) the extension, and open a Nix file. 8 | 1. [Syntax highlighting](./images/docs/nix-syntax-highlight.png) should work out of the box. Nix code blocks inside `markdown` files are also [supported](./images/docs/md-embed-nix.png). 9 | 1. Auto-formatting should work if [`nixfmt`](https://github.com/NixOS/nixfmt) is available in `$PATH`. A custom formatter can be set by [configuring `nix.formatterPath`](#custom-formatter). 10 | 1. Syntax errors are [linted](./images/docs/linting.png) using `nix-instantiate`. 11 | 1. Full language support can be enabled by configuring a language server. See [LSP Plugin Support](#lsp-plugin-support) for more information. 12 | 1. Snippets are provided for conditional expressions, `let`/`with` expressions, and `rec`ursive sets. 13 | 1. Path completion is supported using the [Path Intellisense](https://github.com/ChristianKohler/PathIntellisense) extension. 14 | 15 | ## Settings ⚙️ 16 | 17 | ### LSP Plugin Support 18 | 19 | Full language support can be enabled by using a language server. Generally, any Nix [LSP](https://microsoft.github.io/language-server-protocol/) implementation should work. Some examples are given below for [`nil`](https://github.com/oxalica/nil?tab=readme-ov-file#vscodevscodium-with-nix-ide) and [`nixd`](https://github.com/nix-community/nixd). 20 | 21 | ```json5 22 | { 23 | "nix.enableLanguageServer": true, 24 | "nix.serverPath": "nil", // or "nixd", or ["executable", "argument1", ...] 25 | // LSP config can be passed via the ``nix.serverSettings.{lsp}`` as shown below. 26 | "nix.serverSettings": { 27 | // check https://github.com/oxalica/nil/blob/main/docs/configuration.md for all options available 28 | "nil": { 29 | // "diagnostics": { 30 | // "ignored": ["unused_binding", "unused_with"], 31 | // }, 32 | "formatting": { 33 | "command": ["nixfmt"], 34 | }, 35 | }, 36 | // check https://github.com/nix-community/nixd/blob/main/nixd/docs/configuration.md for all nixd config 37 | "nixd": { 38 | "formatting": { 39 | "command": ["nixfmt"], 40 | }, 41 | "options": { 42 | // By default, this entry will be read from `import { }`. 43 | // You can write arbitrary Nix expressions here, to produce valid "options" declaration result. 44 | // Tip: for flake-based configuration, utilize `builtins.getFlake` 45 | "nixos": { 46 | "expr": "(builtins.getFlake \"/absolute/path/to/flake\").nixosConfigurations..options", 47 | }, 48 | "home-manager": { 49 | "expr": "(builtins.getFlake \"/absolute/path/to/flake\").homeConfigurations..options", 50 | }, 51 | // Tip: use ${workspaceFolder} variable to define path 52 | "nix-darwin": { 53 | "expr": "(builtins.getFlake \"${workspaceFolder}/path/to/flake\").darwinConfigurations..options", 54 | }, 55 | }, 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | ### Custom Formatter 62 | 63 | It can be changed by setting `nix.formatterPath` to any command which can accept file contents on stdin and return formatted text on stdout. 64 | 65 | > [!IMPORTANT] 66 | > If you are using the above LSP plugin setting, then this configuration is not used. 67 | 68 | ```json5 69 | { 70 | "nix.formatterPath": "nixfmt" // or "alejandra" 71 | // or pass full list of args like below 72 | // "nix.formatterPath": ["treefmt", "--stdin", "{file}"] 73 | } 74 | ``` 75 | 76 | ## Contributing 💪 77 | 78 | We welcome contributions to this extension. Kindly start with any of open issues or feature requests. 79 | 80 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. 81 | 82 | ## Credits 83 | 84 | Special thanks to: 85 | 86 | - [@wmertens](https://github.com/wmertens) for [writing the grammar](https://github.com/wmertens/sublime-nix/blob/master/nix.tmLanguage). 87 | - The [vscode-fish](https://github.com/bmalehorn/vscode-fish/) extension, which was modified to work for Nix in this extension. 88 | -------------------------------------------------------------------------------- /src/linter.ts: -------------------------------------------------------------------------------- 1 | import { sync as commandExistsSync } from "command-exists"; 2 | import * as vscode from "vscode"; 3 | import { Diagnostic, type ExtensionContext, type TextDocument } from "vscode"; 4 | import { runInWorkspace } from "./process-runner"; 5 | 6 | /** 7 | * Whether a given document is saved to disk and in Nix language. 8 | * 9 | * @param document The document to check 10 | * @return Whether the document is a Nix document saved to disk 11 | */ 12 | const isSavedDocument = (document: TextDocument): boolean => 13 | !document.isDirty && 14 | 0 < 15 | vscode.languages.match( 16 | { 17 | language: "nix", 18 | scheme: "file", 19 | }, 20 | document, 21 | ); 22 | 23 | interface LintErrorType { 24 | msg: string; 25 | row: number; 26 | col: number; 27 | } 28 | 29 | /** 30 | * Exec pattern against the given text and return an array of all matches. 31 | * 32 | * @param text The output from nix-instantiate stderr 33 | * @return All matches of pattern in text. 34 | */ 35 | const getErrors = (text: string): ReadonlyArray => { 36 | const results = []; 37 | // matches both syntax error messages, like: 38 | // `error: syntax error, unexpected ']', expecting ';', at /home/foo/bar/shell.nix:19:3` 39 | // as well as symbol error messages, like 40 | // `error: undefined variable 'openjdk' at /home/foo/bar/shell.nix:14:5` 41 | const pattern = /^error: (.+) at .+:(\d+):(\d+)$/gm; 42 | // We need to loop through the regexp here, so a let is required 43 | let match = pattern.exec(text); 44 | while (match !== null) { 45 | results.push({ 46 | msg: match[1], 47 | row: Number.parseInt(match[2], 10), 48 | col: Number.parseInt(match[3], 10), 49 | }); 50 | match = pattern.exec(text); 51 | } 52 | return results; 53 | }; 54 | 55 | /** 56 | * Parse errors from output for a given document. 57 | * 58 | * @param document The document to whose contents errors refer 59 | * @param output The error output from shell. 60 | * @return An array of all diagnostics 61 | */ 62 | const shellOutputToDiagnostics = ( 63 | document: TextDocument, 64 | output: string, 65 | ): ReadonlyArray => { 66 | const diagnostics: Array = []; 67 | for (const err of getErrors(output)) { 68 | const range = document.validateRange( 69 | new vscode.Range(err.row - 1, err.col - 2, err.row - 1, err.col + 2), 70 | ); 71 | const diagnostic = new Diagnostic(range, err.msg); 72 | diagnostic.source = "nix"; 73 | diagnostics.push(diagnostic); 74 | } 75 | return diagnostics; 76 | }; 77 | 78 | /** 79 | * Start linting files. 80 | * 81 | * @param context The extension context 82 | */ 83 | export async function startLinting(context: ExtensionContext): Promise { 84 | const diagnostics = vscode.languages.createDiagnosticCollection("nix"); 85 | context.subscriptions.push(diagnostics); 86 | 87 | // Check if nix-instantiate is available 88 | if (!commandExistsSync("nix-instantiate")) { 89 | await vscode.window.showWarningMessage( 90 | "nix-instantiate not found in $PATH. Linting is disabled.", 91 | ); 92 | return; 93 | } 94 | 95 | const lint = async (document: TextDocument) => { 96 | if (isSavedDocument(document)) { 97 | const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); 98 | let d: ReadonlyArray; 99 | try { 100 | const result = await runInWorkspace(workspaceFolder, [ 101 | "nix-instantiate", 102 | "--parse", 103 | document.fileName, 104 | ]); 105 | d = shellOutputToDiagnostics(document, result.stderr); 106 | } catch (error) { 107 | if (error instanceof Error) { 108 | await vscode.window.showErrorMessage(error.message); 109 | } 110 | diagnostics.delete(document.uri); 111 | return; 112 | } 113 | diagnostics.set(document.uri, d as Diagnostic[]); 114 | } 115 | }; 116 | 117 | vscode.workspace.onDidOpenTextDocument(lint, null, context.subscriptions); 118 | vscode.workspace.onDidSaveTextDocument(lint, null, context.subscriptions); 119 | for await (const textDocument of vscode.workspace.textDocuments) { 120 | await lint(textDocument); 121 | } 122 | // Remove diagnostics for closed files 123 | vscode.workspace.onDidCloseTextDocument( 124 | (d) => diagnostics.delete(d.uri), 125 | null, 126 | context.subscriptions, 127 | ); 128 | } 129 | -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "#", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ 7 | "/*", 8 | "*/" 9 | ] 10 | }, 11 | // symbols used as brackets 12 | "brackets": [ 13 | [ 14 | "${", 15 | "}" 16 | ], 17 | [ 18 | "{", 19 | "}" 20 | ], 21 | [ 22 | "[", 23 | "]" 24 | ], 25 | [ 26 | "(", 27 | ")" 28 | ], 29 | [ 30 | "<", 31 | ">" 32 | ] 33 | ], 34 | // symbols that are auto closed when typing 35 | "autoClosingPairs": [ 36 | { 37 | "open": "{", 38 | "close": "}" 39 | }, 40 | { 41 | "open": "[", 42 | "close": "]" 43 | }, 44 | { 45 | "open": "(", 46 | "close": ")" 47 | }, 48 | { 49 | "open": "\"", 50 | "close": "\"", 51 | "notIn": [ 52 | "string" 53 | ] 54 | }, 55 | { 56 | "open": "''", 57 | "close": "''", 58 | "notIn": [ 59 | "string", 60 | "comment" 61 | ] 62 | }, 63 | { 64 | "open": "/**", 65 | "close": " */", 66 | "notIn": [ 67 | "string" 68 | ] 69 | } 70 | ], 71 | "autoCloseBefore": ";:.,=}])>` \n\t\"", 72 | // symbols that can be used to surround a selection 73 | "surroundingPairs": [ 74 | [ 75 | "{", 76 | "}" 77 | ], 78 | [ 79 | "[", 80 | "]" 81 | ], 82 | [ 83 | "(", 84 | ")" 85 | ], 86 | [ 87 | "<", 88 | ">" 89 | ], 90 | [ 91 | "\"", 92 | "\"" 93 | ], 94 | [ 95 | "'", 96 | "'" 97 | ], 98 | [ 99 | "''", 100 | "''" 101 | ], 102 | // Backticks don't have any special meaning in nix language, it's just for Markdown in comments. 103 | [ 104 | "`", 105 | "`" 106 | ], 107 | ], 108 | "folding": { 109 | "markers": { 110 | "start": "^\\s*#\\s*#?region\\b", 111 | "end": "^\\s*#\\s*#?endregion\\b" 112 | } 113 | }, 114 | "onEnterRules": [ 115 | { 116 | "beforeText": "^.*\\blet\\s*$", 117 | "afterText": "\\s*in\\b.*$", 118 | "action": { 119 | "indent": "indentOutdent" 120 | } 121 | }, 122 | { 123 | "beforeText": "^.*\\bif\\s*$", 124 | "afterText": "\\s*then\\b.*$", 125 | "action": { 126 | "indent": "indentOutdent" 127 | } 128 | }, 129 | { 130 | "beforeText": "^.*\\bthen\\s*$", 131 | "afterText": "\\s*else\\b.*$", 132 | "action": { 133 | "indent": "indentOutdent" 134 | } 135 | }, 136 | { 137 | "beforeText": "^.*''\\s*$", 138 | "afterText": "\\s*''.*$", 139 | "action": { 140 | "indent": "indentOutdent" 141 | } 142 | }, 143 | { 144 | "beforeText": "^.*/\\*\\*\\s*$", 145 | "afterText": "\\s*\\*/.*$", 146 | "action": { 147 | "indent": "indentOutdent" 148 | } 149 | }, 150 | { 151 | "beforeText": "^.*(?:=|/\\*\\*|'')\\s*$", 152 | "action": { 153 | "indent": "indent" 154 | } 155 | }, 156 | { 157 | // NOTE: `in` is not included on purpose, because nixfmt also doesn't indent after it. 158 | "beforeText": "^.*\\b(?:let|with|if|then|else|rec|or|and|assert|inherit)\\s*$", 159 | "action": { 160 | "indent": "indent" 161 | } 162 | }, 163 | ], 164 | "wordPattern": "(-?\\d*\\.\\d\\w*)|((~|[^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\<\\>\\/\\?\\s]+)/[^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\<\\>\\?\\s]+)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" 165 | } 166 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | // from PR of https://github.com/nix-community/vscode-nix-ide/pull/16/ 2 | 3 | import { inspect } from "node:util"; 4 | import { sync as commandExistsSync } from "command-exists"; 5 | import { 6 | type Disposable, 7 | type ExtensionContext, 8 | env, 9 | Uri, 10 | window, 11 | workspace, 12 | } from "vscode"; 13 | import type { 14 | CancellationToken, 15 | ConfigurationParams, 16 | LanguageClientOptions, 17 | LSPArray, 18 | MessageSignature, 19 | } from "vscode-languageclient"; 20 | import { 21 | type Executable, 22 | LanguageClient, 23 | type ServerOptions, 24 | } from "vscode-languageclient/node"; 25 | import { config, type UriMessageItem } from "./configuration"; 26 | 27 | class Client extends LanguageClient { 28 | disposables: Disposable[] = []; 29 | 30 | override handleFailedRequest( 31 | type: MessageSignature, 32 | token: CancellationToken | undefined, 33 | error: unknown, 34 | defaultValue: T, 35 | showNotification?: boolean, 36 | ): T { 37 | if (config.hiddenErrorKinds.includes(type.method)) { 38 | this.outputChannel.appendLine( 39 | `Suppressing failed ${inspect(type.method)} notification`, 40 | ); 41 | return super.handleFailedRequest(type, token, error, defaultValue, false); 42 | } 43 | return super.handleFailedRequest( 44 | type, 45 | token, 46 | error, 47 | defaultValue, 48 | showNotification, 49 | ); 50 | } 51 | 52 | override dispose(timeout?: number): Promise { 53 | let timedOut = false; 54 | if (timeout) { 55 | setTimeout(() => { 56 | timedOut = true; 57 | }, timeout); 58 | } 59 | 60 | for (const disposable of this.disposables) { 61 | if (timedOut) { 62 | break; 63 | } 64 | disposable.dispose(); 65 | } 66 | 67 | return Promise.resolve(); 68 | } 69 | } 70 | 71 | let client: Client; 72 | 73 | export async function activate(context: ExtensionContext): Promise { 74 | if (!commandExistsSync(config.serverPath[0])) { 75 | const selection = await window.showErrorMessage( 76 | `Command ${config.serverPath} not found in $PATH`, 77 | { 78 | title: "Install language server", 79 | uri: Uri.parse( 80 | "https://github.com/nix-community/vscode-nix-ide?tab=readme-ov-file#language-servers", 81 | ), 82 | }, 83 | ); 84 | if (selection?.uri !== undefined) { 85 | await env.openExternal(selection?.uri); 86 | return; 87 | } 88 | } 89 | const serverExecutable: Executable = { 90 | command: config.serverPath[0], 91 | args: config.serverPath.slice(1), 92 | }; 93 | const serverOptions: ServerOptions = serverExecutable; 94 | 95 | const nixDocumentSelector: { scheme: string; language: string }[] = [ 96 | { scheme: "file", language: "nix" }, 97 | { scheme: "untitled", language: "nix" }, 98 | ]; 99 | 100 | const outputChannel = window.createOutputChannel("Nix"); 101 | const fileEvents = workspace.createFileSystemWatcher("**/*.nix"); 102 | 103 | const clientOptions: LanguageClientOptions = { 104 | documentSelector: nixDocumentSelector, 105 | synchronize: { 106 | fileEvents, 107 | configurationSection: [config.rootSection], 108 | }, 109 | outputChannel, 110 | middleware: { 111 | workspace: { 112 | configuration: (params: ConfigurationParams): LSPArray[] => { 113 | const items = params.items || []; 114 | const res: LSPArray = []; 115 | const settings = config.serverSettings; 116 | for (const item of items) { 117 | if (!item?.section) { 118 | continue; 119 | } 120 | const sectionSettings = 121 | settings[item.section as keyof typeof settings]; 122 | if (!sectionSettings) { 123 | client.warn( 124 | `failed to find "${item.section}" in "nix.serverSettings"`, 125 | ); 126 | } 127 | res.push(sectionSettings ?? null); 128 | } 129 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 130 | return res; 131 | }, 132 | }, 133 | }, 134 | }; 135 | 136 | client = new Client("nix", "Nix", serverOptions, clientOptions); 137 | client.disposables.push(outputChannel, fileEvents); 138 | client.registerProposedFeatures(); 139 | await client.start(); 140 | 141 | context.subscriptions.push(client); 142 | } 143 | 144 | export async function deactivate(): Promise { 145 | if (client?.needsStop()) { 146 | await client.stop(); 147 | } 148 | await client.dispose(); 149 | } 150 | 151 | export async function restart(context: ExtensionContext): Promise { 152 | const restartingMsg = window.setStatusBarMessage( 153 | "$(loading~spin) Restarting Nix language server", 154 | ); 155 | 156 | try { 157 | await deactivate(); 158 | await activate(context); 159 | } catch (error) { 160 | client?.error("Failed to restart Nix language server", error, "force"); 161 | } finally { 162 | restartingMsg.dispose(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.userosscache 5 | *.sln.docstates 6 | # User-specific files (MonoDevelop/Xamarin Studio) 7 | *.userprefs 8 | # Build results 9 | [Dd]ebug/ 10 | [Dd]ebugPublic/ 11 | [Rr]elease/ 12 | [Rr]eleases/ 13 | x64/ 14 | x86/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | [Ll]og/ 19 | # Visual Studio 2015-2017 cache/options directory 20 | .vs/ 21 | # Uncomment if you have tasks that create the project's static files in wwwroot 22 | # wwwroot/ 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | # NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | # Build Results of an ATL Project 30 | [Dd]ebugPS/ 31 | [Rr]eleasePS/ 32 | dlldata.c 33 | # DNX 34 | project.lock.json 35 | project.fragment.lock.json 36 | artifacts/ 37 | *_i.c 38 | *_p.c 39 | *_i.h 40 | *.ilk 41 | *.meta 42 | *.obj 43 | *.pch 44 | *.pdb 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.tlb 50 | *.tli 51 | *.tlh 52 | *.tmp 53 | *.tmp_proj 54 | *.log 55 | *.vspscc 56 | *.vssscc 57 | .builds 58 | *.pidb 59 | *.svclog 60 | *.scc 61 | # Chutzpah Test files 62 | _Chutzpah* 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opendb 68 | *.opensdf 69 | *.sdf 70 | *.cachefile 71 | *.VC.db 72 | *.VC.VC.opendb 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | *.sap 78 | # TFS Local Workspace 79 | $tf/ 80 | # Guidance Automation Toolkit 81 | *.gpState 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | # JustCode is a .NET coding add-in 87 | .JustCode 88 | # TeamCity is a build add-in 89 | _TeamCity* 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | # Visual Studio code coverage results 93 | *.coverage 94 | *.coveragexml 95 | # NCrunch 96 | _NCrunch_* 97 | .*crunch*.local.xml 98 | nCrunchTemp_* 99 | # MightyMoose 100 | *.mm.* 101 | AutoTest.Net/ 102 | # Web workbench (sass) 103 | .sass-cache/ 104 | # Installshield output folder 105 | [Ee]xpress/ 106 | # DocProject is a documentation generator add-in 107 | DocProject/buildhelp/ 108 | DocProject/Help/*.HxT 109 | DocProject/Help/*.HxC 110 | DocProject/Help/*.hhc 111 | DocProject/Help/*.hhk 112 | DocProject/Help/*.hhp 113 | DocProject/Help/Html2 114 | DocProject/Help/html 115 | # Click-Once directory 116 | publish/ 117 | # Publish Web Output 118 | *.[Pp]ublish.xml 119 | *.azurePubxml 120 | # TODO: Comment the next line if you want to checkin your web deploy settings 121 | # but database connection strings (with potential passwords) will be unencrypted 122 | *.pubxml 123 | *.publishproj 124 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 125 | # checkin your Azure Web App publish settings, but sensitive information contained 126 | # in these scripts will be unencrypted 127 | PublishScripts/ 128 | # NuGet Packages 129 | *.nupkg 130 | # The packages folder can be ignored because of Package Restore 131 | **/packages/* 132 | # except build/, which is used as an MSBuild target. 133 | !**/packages/build/ 134 | # Uncomment if necessary however generally it will be regenerated when needed 135 | #!**/packages/repositories.config 136 | # NuGet v3's project.json files produces more ignoreable files 137 | *.nuget.props 138 | *.nuget.targets 139 | # Microsoft Azure Build Output 140 | csx/ 141 | *.build.csdef 142 | # Microsoft Azure Emulator 143 | ecf/ 144 | rcf/ 145 | # Windows Store app package directories and files 146 | AppPackages/ 147 | BundleArtifacts/ 148 | Package.StoreAssociation.xml 149 | _pkginfo.txt 150 | # Visual Studio cache files 151 | # files ending in .cache can be ignored 152 | *.[Cc]ache 153 | # but keep track of directories ending in .cache 154 | !*.[Cc]ache/ 155 | # Others 156 | ClientBin/ 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.jfm 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | orleans.codegen.cs 166 | # Since there are multiple workflows, uncomment next line to ignore bower_components 167 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 168 | #bower_components/ 169 | # RIA/Silverlight projects 170 | Generated_Code/ 171 | # Backup & report files from converting an old project file 172 | # to a newer Visual Studio version. Backup files are not needed, 173 | # because we have git 😉 174 | _UpgradeReport_Files/ 175 | Backup*/ 176 | UpgradeLog*.XML 177 | UpgradeLog*.htm 178 | # SQL Server files 179 | *.mdf 180 | *.ldf 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | # Microsoft Fakes 186 | FakesAssemblies/ 187 | # GhostDoc plugin setting file 188 | *.GhostDoc.xml 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | # Visual Studio 6 build log 192 | *.plg 193 | # Visual Studio 6 workspace options file 194 | *.opt 195 | # Visual Studio LightSwitch build output 196 | **/*.HTMLClient/GeneratedArtifacts 197 | **/*.DesktopClient/GeneratedArtifacts 198 | **/*.DesktopClient/ModelManifest.xml 199 | **/*.Server/GeneratedArtifacts 200 | **/*.Server/ModelManifest.xml 201 | _Pvt_Extensions 202 | # Paket dependency manager 203 | .paket/paket.exe 204 | paket-files/ 205 | # FAKE - F# Make 206 | .fake/ 207 | # JetBrains Rider 208 | .idea/ 209 | *.sln.iml 210 | # CodeRush 211 | .cr/ 212 | # Python Tools for Visual Studio (PTVS) 213 | __pycache__/ 214 | *.pyc 215 | # Cake - Uncomment if you are using it 216 | -------------------------------------------------------------------------------- /syntax-tests/bug-508.nix.snap: -------------------------------------------------------------------------------- 1 | ># from https://github.com/nix-community/vscode-nix-ide/issues/508 2 | #^ source.nix comment.line.number-sign.nix 3 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.nix comment.line.number-sign.nix 4 | >{ 5 | #^ source.nix punctuation.definition.attrset-or-function.nix 6 | > case_1 = (if 2 > 1 then "a" else "b"); 7 | #^^ source.nix 8 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 9 | # ^ source.nix 10 | # ^ source.nix keyword.operator.bind.nix 11 | # ^ source.nix 12 | # ^ source.nix punctuation.definition.expression.nix 13 | # ^^ source.nix keyword.other.nix 14 | # ^ source.nix 15 | # ^ source.nix constant.numeric.nix 16 | # ^ source.nix 17 | # ^ source.nix keyword.operator.nix 18 | # ^ source.nix 19 | # ^ source.nix constant.numeric.nix 20 | # ^ source.nix 21 | # ^^ source.nix keyword.other.nix 22 | # ^^ source.nix keyword.other.nix 23 | # ^ source.nix 24 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 25 | # ^ source.nix string.quoted.double.nix 26 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 27 | # ^ source.nix 28 | # ^^ source.nix keyword.other.nix 29 | # ^^ source.nix keyword.other.nix 30 | # ^ source.nix 31 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 32 | # ^ source.nix string.quoted.double.nix 33 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 34 | # ^ source.nix punctuation.definition.expression.nix 35 | # ^ source.nix punctuation.terminator.bind.nix 36 | > case_2 = (if 2 < 1 then "a" else "b"); 37 | #^^ source.nix 38 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 39 | # ^ source.nix 40 | # ^ source.nix keyword.operator.bind.nix 41 | # ^ source.nix 42 | # ^ source.nix punctuation.definition.expression.nix 43 | # ^^ source.nix keyword.other.nix 44 | # ^ source.nix 45 | # ^ source.nix constant.numeric.nix 46 | # ^ source.nix 47 | # ^ source.nix keyword.operator.nix 48 | # ^ source.nix 49 | # ^ source.nix constant.numeric.nix 50 | # ^ source.nix 51 | # ^^ source.nix keyword.other.nix 52 | # ^^ source.nix keyword.other.nix 53 | # ^ source.nix 54 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 55 | # ^ source.nix string.quoted.double.nix 56 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 57 | # ^ source.nix 58 | # ^^ source.nix keyword.other.nix 59 | # ^^ source.nix keyword.other.nix 60 | # ^ source.nix 61 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 62 | # ^ source.nix string.quoted.double.nix 63 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 64 | # ^ source.nix punctuation.definition.expression.nix 65 | # ^ source.nix punctuation.terminator.bind.nix 66 | > case_3 = (if 2 >= 1 then "a" else "b"); 67 | #^^ source.nix 68 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 69 | # ^ source.nix 70 | # ^ source.nix keyword.operator.bind.nix 71 | # ^ source.nix 72 | # ^ source.nix punctuation.definition.expression.nix 73 | # ^^ source.nix keyword.other.nix 74 | # ^ source.nix 75 | # ^ source.nix constant.numeric.nix 76 | # ^ source.nix 77 | # ^^ source.nix keyword.operator.nix 78 | # ^ source.nix 79 | # ^ source.nix constant.numeric.nix 80 | # ^ source.nix 81 | # ^^ source.nix keyword.other.nix 82 | # ^^ source.nix keyword.other.nix 83 | # ^ source.nix 84 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 85 | # ^ source.nix string.quoted.double.nix 86 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 87 | # ^ source.nix 88 | # ^^ source.nix keyword.other.nix 89 | # ^^ source.nix keyword.other.nix 90 | # ^ source.nix 91 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 92 | # ^ source.nix string.quoted.double.nix 93 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 94 | # ^ source.nix punctuation.definition.expression.nix 95 | # ^ source.nix punctuation.terminator.bind.nix 96 | > case_4 = (if 2 <= 1 then "a" else "b"); 97 | #^^ source.nix 98 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 99 | # ^ source.nix 100 | # ^ source.nix keyword.operator.bind.nix 101 | # ^ source.nix 102 | # ^ source.nix punctuation.definition.expression.nix 103 | # ^^ source.nix keyword.other.nix 104 | # ^ source.nix 105 | # ^ source.nix constant.numeric.nix 106 | # ^ source.nix 107 | # ^^ source.nix keyword.operator.nix 108 | # ^ source.nix 109 | # ^ source.nix constant.numeric.nix 110 | # ^ source.nix 111 | # ^^ source.nix keyword.other.nix 112 | # ^^ source.nix keyword.other.nix 113 | # ^ source.nix 114 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 115 | # ^ source.nix string.quoted.double.nix 116 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 117 | # ^ source.nix 118 | # ^^ source.nix keyword.other.nix 119 | # ^^ source.nix keyword.other.nix 120 | # ^ source.nix 121 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 122 | # ^ source.nix string.quoted.double.nix 123 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 124 | # ^ source.nix punctuation.definition.expression.nix 125 | # ^ source.nix punctuation.terminator.bind.nix 126 | > case_5 = (true -> true); # (implication operator) 127 | #^^ source.nix 128 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 129 | # ^ source.nix 130 | # ^ source.nix keyword.operator.bind.nix 131 | # ^ source.nix 132 | # ^ source.nix punctuation.definition.expression.nix 133 | # ^^^^ source.nix constant.language.nix 134 | # ^ source.nix 135 | # ^^ source.nix keyword.operator.nix 136 | # ^ source.nix 137 | # ^^^^ source.nix constant.language.nix 138 | # ^ source.nix punctuation.definition.expression.nix 139 | # ^ source.nix punctuation.terminator.bind.nix 140 | # ^ source.nix 141 | # ^ source.nix comment.line.number-sign.nix 142 | # ^^^^^^^^^^^^^^^^^^^^^^^ source.nix comment.line.number-sign.nix 143 | >} 144 | #^ source.nix punctuation.definition.attrset.nix -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nix-ide", 3 | "displayName": "Nix IDE", 4 | "description": "Nix language support - syntax highlighting, formatting, and error reporting.", 5 | "version": "0.5.0", 6 | "publisher": "jnoortheen", 7 | "icon": "images/icon.png", 8 | "license": "MIT", 9 | "engines": { 10 | "vscode": ">=1.96.0" 11 | }, 12 | "categories": [ 13 | "Programming Languages", 14 | "Formatters", 15 | "Snippets" 16 | ], 17 | "keywords": [ 18 | "nix" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/nix-community/vscode-nix-ide/issues" 22 | }, 23 | "homepage": "https://github.com/nix-community/vscode-nix-ide", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/nix-community/vscode-nix-ide" 27 | }, 28 | "main": "dist/extension.js", 29 | "contributes": { 30 | "languages": [ 31 | { 32 | "id": "nix", 33 | "aliases": [ 34 | "Nix", 35 | "nix" 36 | ], 37 | "extensions": [ 38 | ".nix" 39 | ], 40 | "icon": { 41 | "dark": "images/icon.svg", 42 | "light": "images/icon.svg" 43 | }, 44 | "configuration": "./language-configuration.json" 45 | } 46 | ], 47 | "grammars": [ 48 | { 49 | "language": "nix", 50 | "scopeName": "source.nix", 51 | "path": "./dist/nix.tmLanguage.json" 52 | }, 53 | { 54 | "scopeName": "markdown.nix.codeblock", 55 | "path": "./dist/injection.json", 56 | "injectTo": [ 57 | "text.html.markdown" 58 | ], 59 | "embeddedLanguages": { 60 | "meta.embedded.block.nix": "nix" 61 | } 62 | } 63 | ], 64 | "snippets": [ 65 | { 66 | "language": "nix", 67 | "path": "./snippets.json" 68 | } 69 | ], 70 | "configuration": { 71 | "title": "NixIDE", 72 | "properties": { 73 | "nix.formatterPath": { 74 | "default": "nixfmt", 75 | "markdownDescription": "Full path to the nix formatter executable. This setting won't take effect if `nix.enableLanguageServer` is enabled; if that's the case, you can instead set formatter via `nix.serverSettings` (see [README](https://github.com/nix-community/vscode-nix-ide#lsp-plugin-support) for examples)", 76 | "oneOf": [ 77 | { 78 | "type": "string", 79 | "enum": [ 80 | "nixfmt", 81 | "nix3-fmt", 82 | "alejandra", 83 | "treefmt", 84 | "nixpkgs-fmt" 85 | ], 86 | "markdownEnumDescriptions": [ 87 | "[nixfmt](https://github.com/NixOS/nixfmt) - The official formatter for the Nix language", 88 | "[nix3-fmt](https://nix.dev/manual/nix/2.17/command-ref/new-cli/nix3-fmt) - Use the flake configured formatter", 89 | "[alejandra](https://github.com/kamadorueda/alejandra) - The Uncompromising Nix Code Formatter", 90 | "[treefmt](https://github.com/numtide/treefmt-nix) - All in one formatter", 91 | "[nixpkgs-fmt](https://github.com/nix-community/nixpkgs-fmt) - Deprecated as it is no longer maintained" 92 | ] 93 | }, 94 | { 95 | "type": "array", 96 | "items": "string", 97 | "minItems": 1 98 | } 99 | ] 100 | }, 101 | "nix.serverPath": { 102 | "default": "nil", 103 | "description": "Location of the nix language server command.", 104 | "oneOf": [ 105 | { 106 | "type": "string" 107 | }, 108 | { 109 | "type": "array", 110 | "items": "string", 111 | "minItems": 1 112 | } 113 | ] 114 | }, 115 | "nix.enableLanguageServer": { 116 | "type": "boolean", 117 | "default": false, 118 | "description": "Use LSP instead of nix-instantiate and the formatter configured via `nix.formatterPath`." 119 | }, 120 | "nix.serverSettings": { 121 | "type": "object", 122 | "default": {}, 123 | "description": "Settings passed to the language server on configuration requests." 124 | }, 125 | "nix.hiddenLanguageServerErrors": { 126 | "type": "array", 127 | "items": { 128 | "type": "string" 129 | }, 130 | "default": [], 131 | "description": "Error notifications from the language server for these request types will be suppressed.", 132 | "examples": [ 133 | [ 134 | "textDocument/definition", 135 | "textDocument/documentSymbol" 136 | ] 137 | ] 138 | } 139 | } 140 | }, 141 | "configurationDefaults": { 142 | "[nix]": { 143 | "editor.insertSpaces": true, 144 | "editor.tabSize": 2 145 | }, 146 | "explorer.fileNesting.patterns": { 147 | "flake.nix": "flake.lock" 148 | }, 149 | "files.associations": { 150 | "flake.lock": "json" 151 | } 152 | }, 153 | "commands": [ 154 | { 155 | "title": "Restart Language Server", 156 | "category": "Nix IDE", 157 | "command": "nix-ide.restartLanguageServer" 158 | } 159 | ], 160 | "semanticTokenTypes": [ 161 | { 162 | "id": "boolean", 163 | "description": "Style for boolean literals", 164 | "superType": "keywords" 165 | }, 166 | { 167 | "id": "constant", 168 | "description": "Style for `builtins` constants" 169 | }, 170 | { 171 | "id": "path", 172 | "description": "Style for paths" 173 | }, 174 | { 175 | "id": "punctuations", 176 | "description": "Style for punctuations" 177 | } 178 | ], 179 | "semanticTokenModifiers": [ 180 | { 181 | "id": "builtin", 182 | "description": "Style for `builtins` variables and functions" 183 | }, 184 | { 185 | "id": "conditional", 186 | "description": "Style for conditional operators and keywords" 187 | }, 188 | { 189 | "id": "delimiter", 190 | "description": "Style for delimiter punctuations" 191 | }, 192 | { 193 | "id": "escape", 194 | "description": "Style for escape sequences in strings" 195 | }, 196 | { 197 | "id": "parenthesis", 198 | "description": "Style for parenthesis" 199 | }, 200 | { 201 | "id": "unresolved", 202 | "description": "Style for unresolved variables" 203 | }, 204 | { 205 | "id": "withAttribute", 206 | "description": "Style for attributes from `with`" 207 | } 208 | ], 209 | "semanticTokenScopes": [ 210 | { 211 | "language": "nix", 212 | "scopes": { 213 | "boolean": [ 214 | "constant.language.boolean.nix" 215 | ], 216 | "constant.builtin": [ 217 | "support.const.nix" 218 | ], 219 | "function.builtin": [ 220 | "support.function.nix" 221 | ], 222 | "struct.builtin": [ 223 | "support.const.nix" 224 | ], 225 | "path": [ 226 | "constant.other.path.nix" 227 | ], 228 | "string": [ 229 | "string.quoted.double.nix" 230 | ], 231 | "string.escape": [ 232 | "constant.character.escape.nix" 233 | ], 234 | "variable": [ 235 | "variable.other.nix" 236 | ], 237 | "variable.unresolved": [ 238 | "invalid.nix" 239 | ], 240 | "parameter": [ 241 | "variable.parameter.name.nix" 242 | ], 243 | "property": [ 244 | "entity.other.attribute-name.single.nix" 245 | ], 246 | "*.withAttribute": [ 247 | "markup.underline" 248 | ], 249 | "operator": [ 250 | "entity.name.operator.nix" 251 | ] 252 | } 253 | } 254 | ] 255 | }, 256 | "devDependencies": { 257 | "@commitlint/cli": "^20.1.0", 258 | "@commitlint/config-conventional": "^20.0.0", 259 | "@types/bun": "^1.3.0", 260 | "@types/command-exists": "^1.2.3", 261 | "@types/node": "^24.0.3", 262 | "@types/vscode": "^1.96.0", 263 | "@vscode/test-cli": "^0.0.12", 264 | "@vscode/test-electron": "^2.5.2", 265 | "@vscode/vsce": "^3.5.0", 266 | "js-yaml": "^4.1.0", 267 | "lefthook": "^2.0.2", 268 | "ovsx": "^0.10.4", 269 | "tmlanguage-generator": "^0.6.4", 270 | "typescript": "^5.8.3", 271 | "vscode-tmgrammar-test": "^0.1.3", 272 | "yaml": "^2.8.1" 273 | }, 274 | "scripts": { 275 | "prebuild": "mkdir -p dist && bun run src/grammar/build.ts", 276 | "build": "bun build src/extension.ts --outdir=dist --external=vscode --target=node --format=cjs", 277 | "postinstall": "lefthook install", 278 | "clean": "rm -rd dist", 279 | "prepackage": "rm -f *.vsix", 280 | "package": "bun run build --minify && bun run vsce package --no-dependencies", 281 | "publish:ovsx": "ovsx publish *.vsix --pat '$OVS_PAT'", 282 | "publish:vsce": "vsce publish", 283 | "publish": "bun run package && bun run publish:vsce && bun run publish:ovsx", 284 | "test": "bun run build --sourcemap && vscode-tmgrammar-snap 'syntax-tests/*.nix'", 285 | "full-test": "vscode-test", 286 | "pretest": "bun x biome check --write src" 287 | }, 288 | "dependencies": { 289 | "command-exists": "^1.2.9", 290 | "vscode-languageclient": "^9.0.1", 291 | "vscode-variables": "^1.0.1" 292 | } 293 | } -------------------------------------------------------------------------------- /syntax-tests/colon2.nix.snap: -------------------------------------------------------------------------------- 1 | >{ 2 | #^ source.nix punctuation.definition.attrset-or-function.nix 3 | > nps.stacks.monitoring.prometheus.config = lib.mkIf cfg.enablePrometheusExport { 4 | #^^ source.nix 5 | # ^^^ source.nix entity.other.attribute-name.multipart.nix 6 | # ^ source.nix 7 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 8 | # ^ source.nix 9 | # ^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 10 | # ^ source.nix 11 | # ^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 12 | # ^ source.nix 13 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 14 | # ^ source.nix 15 | # ^ source.nix keyword.operator.bind.nix 16 | # ^ source.nix 17 | # ^^^ source.nix variable.parameter.name.nix 18 | # ^ source.nix keyword.operator.nix 19 | # ^^^^ source.nix variable.parameter.name.nix 20 | # ^ source.nix 21 | # ^^^ source.nix variable.parameter.name.nix 22 | # ^ source.nix keyword.operator.nix 23 | # ^^^^^^^^^^^^^^^^^^^^^^ source.nix variable.parameter.name.nix 24 | # ^ source.nix 25 | # ^ source.nix punctuation.definition.attrset-or-function.nix 26 | > scrape_configs = [ 27 | #^^^^ source.nix 28 | # ^^^^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 29 | # ^ source.nix 30 | # ^ source.nix keyword.operator.bind.nix 31 | # ^ source.nix 32 | # ^ source.nix punctuation.definition.list.nix 33 | > { 34 | #^^^^^^ source.nix 35 | # ^ source.nix punctuation.definition.attrset-or-function.nix 36 | > job_name = "blocky"; 37 | #^^^^^^^^ source.nix 38 | # ^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 39 | # ^ source.nix 40 | # ^ source.nix keyword.operator.bind.nix 41 | # ^ source.nix 42 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 43 | # ^^^^^^ source.nix string.quoted.double.nix 44 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 45 | # ^ source.nix punctuation.terminator.bind.nix 46 | > honor_timestamps = true; 47 | #^^^^^^^^ source.nix 48 | # ^^^^^^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 49 | # ^ source.nix 50 | # ^ source.nix keyword.operator.bind.nix 51 | # ^ source.nix 52 | # ^^^^ source.nix constant.language.nix 53 | # ^ source.nix punctuation.terminator.bind.nix 54 | > metrics_path = "/metrics"; 55 | #^^^^^^^^ source.nix 56 | # ^^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 57 | # ^ source.nix 58 | # ^ source.nix keyword.operator.bind.nix 59 | # ^ source.nix 60 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 61 | # ^^^^^^^^ source.nix string.quoted.double.nix 62 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 63 | # ^ source.nix punctuation.terminator.bind.nix 64 | > scheme = "http"; 65 | #^^^^^^^^ source.nix 66 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 67 | # ^ source.nix 68 | # ^ source.nix keyword.operator.bind.nix 69 | # ^ source.nix 70 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 71 | # ^^^^ source.nix string.quoted.double.nix 72 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 73 | # ^ source.nix punctuation.terminator.bind.nix 74 | > static_configs = [ { targets = [ "${name}:6000" ]; } ]; 75 | #^^^^^^^^ source.nix 76 | # ^^^^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 77 | # ^ source.nix 78 | # ^ source.nix keyword.operator.bind.nix 79 | # ^ source.nix 80 | # ^ source.nix punctuation.definition.list.nix 81 | # ^ source.nix 82 | # ^ source.nix punctuation.definition.attrset.nix 83 | # ^ source.nix 84 | # ^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 85 | # ^ source.nix 86 | # ^ source.nix keyword.operator.bind.nix 87 | # ^ source.nix 88 | # ^ source.nix punctuation.definition.list.nix 89 | # ^ source.nix 90 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 91 | # ^^ source.nix string.quoted.double.nix meta.embedded punctuation.section.embedded.begin.nix 92 | # ^^^^ source.nix string.quoted.double.nix meta.embedded variable.parameter.name.nix 93 | # ^ source.nix string.quoted.double.nix meta.embedded punctuation.section.embedded.end.nix 94 | # ^^^^^ source.nix string.quoted.double.nix 95 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 96 | # ^ source.nix 97 | # ^ source.nix punctuation.definition.list.nix 98 | # ^ source.nix punctuation.terminator.bind.nix 99 | # ^ source.nix 100 | # ^ source.nix punctuation.definition.attrset.nix 101 | # ^ source.nix 102 | # ^ source.nix punctuation.definition.list.nix 103 | # ^ source.nix punctuation.terminator.bind.nix 104 | > shell = /* sh */ '' 105 | #^^^^^^^^ source.nix 106 | # ^^^^^ source.nix entity.other.attribute-name.multipart.nix 107 | # ^ source.nix 108 | # ^ source.nix keyword.operator.bind.nix 109 | # ^ source.nix 110 | # ^^^^^^ source.nix comment.block.nix 111 | # ^^ source.nix comment.block.nix 112 | # ^ source.nix 113 | # ^^ source.nix string.quoted.other.nix punctuation.definition.string.other.start.nix 114 | > echo "Starting Prometheus scrape for Blocky at ${name}:6000" 115 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.nix string.quoted.other.nix 116 | # ^^ source.nix string.quoted.other.nix meta.embedded punctuation.section.embedded.begin.nix 117 | # ^^^^ source.nix string.quoted.other.nix meta.embedded variable.parameter.name.nix 118 | # ^ source.nix string.quoted.other.nix meta.embedded punctuation.section.embedded.end.nix 119 | # ^^^^^^^ source.nix string.quoted.other.nix 120 | > ''; 121 | #^^^^^^^^ source.nix string.quoted.other.nix 122 | # ^^ source.nix string.quoted.other.nix punctuation.definition.string.other.end.nix 123 | # ^ source.nix punctuation.terminator.bind.nix 124 | > } 125 | #^^^^^^ source.nix 126 | # ^ source.nix punctuation.definition.attrset.nix 127 | > ]; 128 | #^^^^ source.nix 129 | # ^ source.nix punctuation.definition.list.nix 130 | # ^ source.nix punctuation.terminator.bind.nix 131 | > }; 132 | #^^ source.nix 133 | # ^ source.nix punctuation.definition.attrset.nix 134 | # ^ source.nix punctuation.terminator.bind.nix 135 | > nps.stacks.${name} = { 136 | #^^ source.nix 137 | # ^^^ source.nix entity.other.attribute-name.multipart.nix 138 | # ^ source.nix 139 | # ^^^^^^ source.nix entity.other.attribute-name.multipart.nix 140 | # ^ source.nix 141 | # ^^ source.nix meta.embedded punctuation.section.embedded.begin.nix 142 | # ^^^^ source.nix meta.embedded variable.parameter.name.nix 143 | # ^ source.nix meta.embedded punctuation.section.embedded.end.nix 144 | # ^ source.nix 145 | # ^ source.nix keyword.operator.bind.nix 146 | # ^ source.nix 147 | # ^ source.nix punctuation.definition.attrset-or-function.nix 148 | > settings = { 149 | #^^^^ source.nix 150 | # ^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 151 | # ^ source.nix 152 | # ^ source.nix keyword.operator.bind.nix 153 | # ^ source.nix 154 | # ^ source.nix punctuation.definition.attrset-or-function.nix 155 | > prometheus = { 156 | #^^^^^^ source.nix 157 | # ^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 158 | # ^ source.nix 159 | # ^ source.nix keyword.operator.bind.nix 160 | # ^ source.nix 161 | # ^ source.nix punctuation.definition.attrset-or-function.nix 162 | > enabled = cfg.enablePrometheusExport; 163 | #^^^^^^^^ source.nix 164 | # ^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 165 | # ^ source.nix 166 | # ^ source.nix keyword.operator.bind.nix 167 | # ^ source.nix 168 | # ^^^ source.nix variable.parameter.name.nix 169 | # ^ source.nix keyword.operator.nix 170 | # ^^^^^^^^^^^^^^^^^^^^^^ source.nix variable.parameter.name.nix 171 | # ^ source.nix punctuation.terminator.bind.nix 172 | > level = "aggregated"; 173 | #^^^^^^^^ source.nix 174 | # ^^^^^ source.nix entity.other.attribute-name.multipart.nix 175 | # ^ source.nix 176 | # ^ source.nix keyword.operator.bind.nix 177 | # ^ source.nix 178 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 179 | # ^^^^^^^^^^ source.nix string.quoted.double.nix 180 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 181 | # ^ source.nix punctuation.terminator.bind.nix 182 | > listen_address = "0.0.0.0"; 183 | #^^^^^^^^ source.nix 184 | # ^^^^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 185 | # ^ source.nix 186 | # ^ source.nix keyword.operator.bind.nix 187 | # ^ source.nix 188 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.start.nix 189 | # ^^^^^^^ source.nix string.quoted.double.nix 190 | # ^ source.nix string.quoted.double.nix punctuation.definition.string.double.end.nix 191 | # ^ source.nix punctuation.terminator.bind.nix 192 | > listen_port = 6060; 193 | #^^^^^^^^ source.nix 194 | # ^^^^^^^^^^^ source.nix entity.other.attribute-name.multipart.nix 195 | # ^ source.nix 196 | # ^ source.nix keyword.operator.bind.nix 197 | # ^ source.nix 198 | # ^^^^ source.nix constant.numeric.nix 199 | # ^ source.nix punctuation.terminator.bind.nix 200 | > }; 201 | #^^^^^^ source.nix 202 | # ^ source.nix punctuation.definition.attrset.nix 203 | # ^ source.nix punctuation.terminator.bind.nix 204 | > }; 205 | #^^^^ source.nix 206 | # ^ source.nix punctuation.definition.attrset.nix 207 | # ^ source.nix punctuation.terminator.bind.nix 208 | > }; 209 | #^^ source.nix 210 | # ^ source.nix punctuation.definition.attrset.nix 211 | # ^ source.nix punctuation.terminator.bind.nix 212 | >} 213 | #^ source.nix punctuation.definition.attrset.nix 214 | > -------------------------------------------------------------------------------- /syntax-tests/misc.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | let 3 | inherit (builtins) head tail isList isAttrs isInt attrNames; 4 | 5 | in 6 | 7 | with lib.lists; 8 | with lib.attrsets; 9 | with lib.strings; 10 | 11 | rec { 12 | 13 | # returns default if env var is not set 14 | maybeEnv = name: default: 15 | let value = builtins.getEnv name; in 16 | if value == "" then default else value; 17 | 18 | defaultMergeArg = x : y: if builtins.isAttrs y then 19 | y 20 | else 21 | (y x); 22 | defaultMerge = x: y: x // (defaultMergeArg x y); 23 | foldArgs = merger: f: init: x: 24 | let arg = (merger init (defaultMergeArg init x)); 25 | # now add the function with composed args already applied to the final attrs 26 | base = (setAttrMerge "passthru" {} (f arg) 27 | ( z: z // {__id_static="0.4553087592557379";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 28 | 29 | function = foldArgs merger f arg; 30 | args = (lib.attrByPath ["passthru" "args"] {} z) // x; 31 | } )); 32 | withStdOverrides = base // {__id_static="0.8985474209071187";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 33 | 34 | override = base.passthru.function; 35 | }; 36 | in 37 | withStdOverrides; 38 | 39 | 40 | # shortcut for attrByPath ["name"] default attrs 41 | maybeAttrNullable = maybeAttr; 42 | 43 | # shortcut for attrByPath ["name"] default attrs 44 | maybeAttr = name: default: attrs: attrs.${name} or default; 45 | 46 | 47 | # Return the second argument if the first one is true or the empty version 48 | # of the second argument. 49 | ifEnable = cond: val: 50 | if cond then val 51 | else if builtins.isList val then [] 52 | else if builtins.isAttrs val then {} 53 | # else if builtins.isString val then "" 54 | else if val == true || val == false then false 55 | else null; 56 | 57 | 58 | # Return true only if there is an attribute and it is true. 59 | checkFlag = attrSet: name: 60 | if name == "true" then true else 61 | if name == "false" then false else 62 | if (elem name (attrByPath ["flags"] [] attrSet)) then true else 63 | attrByPath [name] false attrSet ; 64 | 65 | 66 | # Input : attrSet, [ [name default] ... ], name 67 | # Output : its value or default. 68 | getValue = attrSet: argList: name: 69 | ( attrByPath [name] (if checkFlag attrSet name then true else 70 | if argList == [] then null else 71 | let x = builtins.head argList; in 72 | if (head x) == name then 73 | (head (tail x)) 74 | else (getValue attrSet 75 | (tail argList) name)) attrSet ); 76 | 77 | 78 | # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ] 79 | # Output : are reqs satisfied? It's asserted. 80 | checkReqs = attrSet: argList: condList: 81 | ( 82 | foldr lib.and true 83 | (map (x: let name = (head x); in 84 | 85 | ((checkFlag attrSet name) -> 86 | (foldr lib.and true 87 | (map (y: let val=(getValue attrSet argList y); in 88 | (val!=null) && (val!=false)) 89 | (tail x))))) condList)); 90 | 91 | 92 | # This function has O(n^2) performance. 93 | uniqList = { inputList, acc ? [] }: 94 | let go = xs: acc: 95 | if xs == [] 96 | then [] 97 | else let x = head xs; 98 | y = if elem x acc then [] else [x]; 99 | in y ++ go (tail xs) (y ++ acc); 100 | in go inputList acc; 101 | 102 | uniqListExt = { inputList, 103 | outputList ? [], 104 | getter ? (x: x), 105 | compare ? (x: y: x==y) }: 106 | if inputList == [] then outputList else 107 | let x = head inputList; 108 | isX = y: (compare (getter y) (getter x)); 109 | newOutputList = outputList ++ 110 | (if any isX outputList then [] else [x]); 111 | in uniqListExt {__id_static="0.5416861884942961";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 112 | outputList = newOutputList; 113 | inputList = (tail inputList); 114 | inherit getter compare; 115 | }; 116 | 117 | condConcat = name: list: checker: 118 | if list == [] then name else 119 | if checker (head list) then 120 | condConcat 121 | (name + (head (tail list))) 122 | (tail (tail list)) 123 | checker 124 | else condConcat 125 | name (tail (tail list)) checker; 126 | 127 | lazyGenericClosure = {startSet, operator}: 128 | let 129 | work = list: doneKeys: result: 130 | if list == [] then 131 | result 132 | else 133 | let x = head list; key = x.key; in 134 | if elem key doneKeys then 135 | work (tail list) doneKeys result 136 | else 137 | work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result); 138 | in 139 | work startSet [] []; 140 | 141 | innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else 142 | innerModifySumArgs f x (a // b); 143 | modifySumArgs = f: x: innerModifySumArgs f x {}; 144 | 145 | 146 | innerClosePropagation = acc: xs: 147 | if xs == [] 148 | then acc 149 | else let y = head xs; 150 | ys = tail xs; 151 | in if ! isAttrs y 152 | then innerClosePropagation acc ys 153 | else let acc' = [y] ++ acc; 154 | in innerClosePropagation 155 | acc' 156 | (uniqList {__id_static="0.43591725975550166";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 157 | inputList = (maybeAttrNullable "propagatedBuildInputs" [] y) 158 | ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y) 159 | ++ ys; 160 | acc = acc'; 161 | } 162 | ); 163 | 164 | closePropagationSlow = list: (uniqList {__id_static="0.4333385880390894";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 165 | inputList = (innerClosePropagation [] list);}); 166 | 167 | # This is an optimisation of lib.closePropagation which avoids the O(n^2) behavior 168 | # Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs 169 | # The ordering / sorting / comparison is done based on the `outPath` 170 | # attribute of each derivation. 171 | # On some benchmarks, it performs up to 15 times faster than lib.closePropagation. 172 | # See https://github.com/NixOS/nixpkgs/pull/194391 for details. 173 | closePropagationFast = list: 174 | builtins.map (x: x.val) (builtins.genericClosure {__id_static="0.4393836786265646";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 175 | 176 | startSet = builtins.map (x: {__id_static="0.42616742269720653";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 177 | 178 | key = x.outPath; 179 | val = x; 180 | }) (builtins.filter (x: x != null) list); 181 | operator = item: 182 | if !builtins.isAttrs item.val then 183 | [ ] 184 | else 185 | builtins.concatMap (x: 186 | if x != null then [{__id_static="0.7684626401674357";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 187 | 188 | key = x.outPath; 189 | val = x; 190 | }] else 191 | [ ]) ((item.val.propagatedBuildInputs or [ ]) 192 | ++ (item.val.propagatedNativeBuildInputs or [ ])); 193 | }); 194 | 195 | closePropagation = if builtins ? genericClosure 196 | then closePropagationFast 197 | else closePropagationSlow; 198 | 199 | # calls a function (f attr value ) for each record item. returns a list 200 | mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r); 201 | 202 | # attribute set containing one attribute 203 | nvs = name: value: listToAttrs [ (nameValuePair name value) ]; 204 | # adds / replaces an attribute of an attribute set 205 | setAttr = set: name: v: set // (nvs name v); 206 | 207 | # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name) 208 | # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; } 209 | # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; } 210 | setAttrMerge = name: default: attrs: f: 211 | setAttr attrs name (f (maybeAttr name default attrs)); 212 | 213 | # Using f = a: b = b the result is similar to // 214 | # merge attributes with custom function handling the case that the attribute 215 | # exists in both sets 216 | mergeAttrsWithFunc = f: set1: set2: 217 | foldr (n: set: if set ? ${n} 218 | then setAttr set n (f set.${n} set2.${n}) 219 | else set ) 220 | (set2 // set1) (attrNames set2); 221 | 222 | # merging two attribute set concatenating the values of same attribute names 223 | # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; } 224 | mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) ); 225 | 226 | # merges attributes using //, if a name exists in both attributes 227 | # an error will be triggered unless its listed in mergeLists 228 | # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get 229 | # { buildInputs = [a b]; } 230 | # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs? 231 | # in these cases the first buildPhase will override the second one 232 | # ! deprecated, use mergeAttrByFunc instead 233 | mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"], 234 | overrideSnd ? [ "buildPhase" ] 235 | }: attrs1: attrs2: 236 | foldr (n: set: 237 | setAttr set n ( if set ? ${n} 238 | then # merge 239 | if elem n mergeLists # attribute contains list, merge them by concatenating 240 | then attrs2.${n} ++ attrs1.${n} 241 | else if elem n overrideSnd 242 | then attrs1.${n} 243 | else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined" 244 | else attrs2.${n} # add attribute not existing in attr1 245 | )) attrs1 (attrNames attrs2); 246 | 247 | 248 | # example usage: 249 | # mergeAttrByFunc { 250 | # inherit mergeAttrBy; # defined below 251 | # buildInputs = [ a b ]; 252 | # } { 253 | # buildInputs = [ c d ]; 254 | # }; 255 | # will result in 256 | # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; } 257 | # is used by defaultOverridableDelayableArgs and can be used when composing using 258 | # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix 259 | mergeAttrByFunc = x: y: 260 | let 261 | mergeAttrBy2 = {__id_static="0.11202360130761346";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 262 | mergeAttrBy = lib.mergeAttrs; } 263 | // (maybeAttr "mergeAttrBy" {} x) 264 | // (maybeAttr "mergeAttrBy" {} y); in 265 | foldr lib.mergeAttrs {} [ 266 | x y 267 | (mapAttrs ( a: v: # merge special names using given functions 268 | if x ? ${a} 269 | then if y ? ${a} 270 | then v x.${a} y.${a} # both have attr, use merge func 271 | else x.${a} # only x has attr 272 | else y.${a} # only y has attr) 273 | ) (removeAttrs mergeAttrBy2 274 | # don't merge attrs which are neither in x nor y 275 | (filter (a: ! x ? ${a} && ! y ? ${a}) 276 | (attrNames mergeAttrBy2)) 277 | ) 278 | ) 279 | ]; 280 | mergeAttrsByFuncDefaults = foldl mergeAttrByFunc {__id_static="0.6726806392463629";__id_dynamic=builtins.hashFile "sha256" /Users/jeffhykin/repos/snowball/random.ignore; 281 | inherit mergeAttrBy; }; 282 | mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"]; 283 | 284 | # sane defaults (same name as attr name so that inherit can be used) 285 | mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } 286 | listToAttrs (map (n: nameValuePair n lib.concat) 287 | [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ]) 288 | // listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]) 289 | // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ]) 290 | ; 291 | 292 | nixType = x: 293 | if isAttrs x then 294 | if x ? outPath then "derivation" 295 | else "attrs" 296 | else if lib.isFunction x then "function" 297 | else if isList x then "list" 298 | else if x == true then "bool" 299 | else if x == false then "bool" 300 | else if x == null then "null" 301 | else if isInt x then "int" 302 | else "string"; 303 | 304 | /* deprecated: 305 | 306 | For historical reasons, imap has an index starting at 1. 307 | 308 | But for consistency with the rest of the library we want an index 309 | starting at zero. 310 | */ 311 | imap = imap1; 312 | 313 | # Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial 314 | fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 315 | fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000"; 316 | fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; 317 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.5.0](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.24...v0.5.0) (2025-10-10) 6 | 7 | 8 | ### ⚠ BREAKING CHANGES 9 | 10 | * remove special highlight of comment remarks like todo/fixme 11 | 12 | ### Features 13 | 14 | * allow "serverPath" to be a list of arguments ([#509](https://github.com/nix-community/vscode-nix-ide/issues/509)) ([6a1835a](https://github.com/nix-community/vscode-nix-ide/commit/6a1835a5b5c85b1d9de4fbf2480b315095a19a30)) 15 | * **manifest:** provide `flake.lock` format ([#507](https://github.com/nix-community/vscode-nix-ide/issues/507)) ([79876a0](https://github.com/nix-community/vscode-nix-ide/commit/79876a0fa762968a9816235f0bcf88c91a7640dd)) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **syntax:** syntax highlighting issue with colon after interpolation in nested attrset ([aa8a59d](https://github.com/nix-community/vscode-nix-ide/commit/aa8a59d845efa5bbe69713dfddadd345611e632b)), closes [#355](https://github.com/nix-community/vscode-nix-ide/issues/355) 21 | 22 | 23 | * remove special highlight of comment remarks like todo/fixme ([fe0a576](https://github.com/nix-community/vscode-nix-ide/commit/fe0a5763cbe937ffcb00846b053b9928fc919f7e)), closes [#476](https://github.com/nix-community/vscode-nix-ide/issues/476) 24 | 25 | ### [0.4.24](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.23...v0.4.24) (2025-09-29) 26 | 27 | 28 | ### Features 29 | 30 | * set explorer.fileNesting for flake.lock ([#498](https://github.com/nix-community/vscode-nix-ide/issues/498)) ([05b33f2](https://github.com/nix-community/vscode-nix-ide/commit/05b33f2c4c8ca266b02b99fa2771810c7393e649)) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * vscode debug launch ([#500](https://github.com/nix-community/vscode-nix-ide/issues/500)) ([620cb85](https://github.com/nix-community/vscode-nix-ide/commit/620cb855221e4f310dc8e470ac366139ad243e1c)) 36 | 37 | ### [0.4.23](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.22...v0.4.23) (2025-09-16) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * nix file icon off center in VS code ([#495](https://github.com/nix-community/vscode-nix-ide/issues/495)) ([fc62423](https://github.com/nix-community/vscode-nix-ide/commit/fc62423d9f8f92dace4b6e70b0d83b9f0e728131)) 43 | 44 | ### [0.4.22](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.21...v0.4.22) (2025-06-29) 45 | 46 | 47 | ### Features 48 | 49 | * add backticks to surroundingPairs ([#491](https://github.com/nix-community/vscode-nix-ide/issues/491)) ([b8744ab](https://github.com/nix-community/vscode-nix-ide/commit/b8744ab40ca31a2012a9238d063a3d4bacfbde7c)) 50 | 51 | ### [0.4.21](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.20...v0.4.21) (2025-06-20) 52 | 53 | ### [0.4.20](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.19...v0.4.20) (2025-06-20) 54 | 55 | ### [0.4.19](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.18...v0.4.19) (2025-06-20) 56 | 57 | 58 | ### Features 59 | 60 | * add semantic token default scopes for nil ([#487](https://github.com/nix-community/vscode-nix-ide/issues/487)) ([48d4934](https://github.com/nix-community/vscode-nix-ide/commit/48d493465c67890a035090ea861548a49f6108dd)) 61 | 62 | ### [0.4.18](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.17...v0.4.18) (2025-05-19) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * add auto-indent for multi-line strings ([#480](https://github.com/nix-community/vscode-nix-ide/issues/480)) ([1ff3ff0](https://github.com/nix-community/vscode-nix-ide/commit/1ff3ff0a6c769bffba1308731fb81dbe81a39378)) 68 | 69 | ### [0.4.17](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.16...v0.4.17) (2025-05-19) 70 | 71 | 72 | ### Features 73 | 74 | * auto-indent on pressed enter ([#477](https://github.com/nix-community/vscode-nix-ide/issues/477)) ([0c49e90](https://github.com/nix-community/vscode-nix-ide/commit/0c49e907c5537d04adde8e25e94f514adeca20d8)) 75 | 76 | 77 | ### Bug Fixes 78 | 79 | * clarify descriptions of formatter settings ([#478](https://github.com/nix-community/vscode-nix-ide/issues/478)) ([4afa347](https://github.com/nix-community/vscode-nix-ide/commit/4afa3478570787c1e30cc7bea90543173be7fa21)) 80 | * shell.nix with bun instead of yarn (fixes [#473](https://github.com/nix-community/vscode-nix-ide/issues/473)) ([#474](https://github.com/nix-community/vscode-nix-ide/issues/474)) ([62b6e93](https://github.com/nix-community/vscode-nix-ide/commit/62b6e939ddac903a65273d2fc926619a724c86c8)) 81 | 82 | ### [0.4.16](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.15...v0.4.16) (2025-03-18) 83 | 84 | ### [0.4.15](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.14...v0.4.15) (2025-03-18) 85 | 86 | ### [0.4.14](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.13...v0.4.14) (2025-03-18) 87 | 88 | ### [0.4.13](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.12...v0.4.13) (2025-03-18) 89 | 90 | 91 | ### Bug Fixes 92 | 93 | * issue warning on missing `nil`/`nixd` ([#465](https://github.com/nix-community/vscode-nix-ide/issues/465)) ([2cb1d94](https://github.com/nix-community/vscode-nix-ide/commit/2cb1d94f6738efced607e772e0ea5c77b9fc7701)) 94 | 95 | ### [0.4.12](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.11...v0.4.12) (2025-02-02) 96 | 97 | 98 | ### Features 99 | 100 | * add pipe operators ([#458](https://github.com/nix-community/vscode-nix-ide/issues/458)) ([ac1ed65](https://github.com/nix-community/vscode-nix-ide/commit/ac1ed65aa92655748200cd62bd54e5ca051781bc)) 101 | 102 | ### [0.4.11](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.10...v0.4.11) (2025-01-26) 103 | 104 | 105 | ### Features 106 | 107 | * add predefined formatters ([0352918](https://github.com/nix-community/vscode-nix-ide/commit/0352918d9a3dc14af18fd656e11e970c8efd0bf0)) 108 | 109 | 110 | ### Bug Fixes 111 | 112 | * build as cjs module ([1952078](https://github.com/nix-community/vscode-nix-ide/commit/1952078345ab0e2fedbde694a81c6e194d377545)), closes [#453](https://github.com/nix-community/vscode-nix-ide/issues/453) 113 | 114 | ### [0.4.10](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.9...v0.4.10) (2025-01-24) 115 | 116 | ### [0.4.9](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.8...v0.4.9) (2025-01-24) 117 | 118 | ### [0.4.8](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.7...v0.4.8) (2025-01-24) 119 | 120 | ### [0.4.7](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.6...v0.4.7) (2025-01-24) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * automated release by PR ([d9d9070](https://github.com/nix-community/vscode-nix-ide/commit/d9d90704701145f350d6f6ed747d95f8dc33d301)) 126 | 127 | ### [0.4.6](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.5...v0.4.6) (2025-01-24) 128 | 129 | ### [0.4.5](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.3...v0.4.5) (2025-01-24) 130 | 131 | ### [0.4.4](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.2...v0.4.4) (2025-01-24) 132 | 133 | 134 | ### Features 135 | 136 | * do not package syntax *yml files ([ce091fb](https://github.com/nix-community/vscode-nix-ide/commit/ce091fbab288407dfa678dbba8f1b84ce2bfda38)) 137 | * replace esbuild with `bun build` ([20c7eb3](https://github.com/nix-community/vscode-nix-ide/commit/20c7eb3174a0d151abee22d9252dbd8ea4d65a54)) 138 | * use bun v1.2 text lockfiles ([da6fd4d](https://github.com/nix-community/vscode-nix-ide/commit/da6fd4dfb64b766bca9aaff70bf3cd9a2fb6a5b2)) 139 | 140 | ### [0.4.3](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.2...v0.4.3) (2025-01-24) 141 | 142 | 143 | ### Features 144 | 145 | * do not package syntax *yml files ([ce091fb](https://github.com/nix-community/vscode-nix-ide/commit/ce091fbab288407dfa678dbba8f1b84ce2bfda38)) 146 | * replace esbuild with `bun build` ([20c7eb3](https://github.com/nix-community/vscode-nix-ide/commit/20c7eb3174a0d151abee22d9252dbd8ea4d65a54)) 147 | * use bun v1.2 text lockfiles ([da6fd4d](https://github.com/nix-community/vscode-nix-ide/commit/da6fd4dfb64b766bca9aaff70bf3cd9a2fb6a5b2)) 148 | 149 | ## [0.4.2](https://github.com/nix-community/vscode-nix-ide/compare/v0.4.1...v0.4.2) (2025-01-17) 150 | 151 | 152 | ### Features 153 | 154 | * remove unused dependencies ([0a842f6](https://github.com/nix-community/vscode-nix-ide/commit/0a842f6687dedb1a7a1ac0fdfde06e390aab6042)) 155 | 156 | ## [0.4.0](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.7...v0.4.0) (2025-01-17) 157 | 158 | 159 | ### Features 160 | 161 | * add grammar test ([018be88](https://github.com/nix-community/vscode-nix-ide/commit/018be88ba0d6d0104e9d57b9250bb36c84ea52ed)) 162 | * add initial integration test ([e25320f](https://github.com/nix-community/vscode-nix-ide/commit/e25320f9e6c2979d94b05c7ef09fd5380d74de71)) 163 | * add release-please action ([50b5921](https://github.com/nix-community/vscode-nix-ide/commit/50b59213beb401641d8ae17bcd4d6e4ef1e28815)) 164 | 165 | 166 | ### Bug Fixes 167 | 168 | * default to nixfmt formatter ([#441](https://github.com/nix-community/vscode-nix-ide/issues/441)) ([582c364](https://github.com/nix-community/vscode-nix-ide/commit/582c3642df2188c3096ac646c575e386ae04923e)) 169 | 170 | ## [0.3.7](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.6...v0.3.7) (2025-01-16) 171 | 172 | 173 | ### Bug Fixes 174 | 175 | * biome linter fixes ([e5c15ed](https://github.com/nix-community/vscode-nix-ide/commit/e5c15edad557f08f69178d466a42201c753d4e6d)) 176 | 177 | ## [0.3.6](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.5...v0.3.6) (2024-09-20) 178 | 179 | 180 | ### Features 181 | 182 | * delay activation ([#424](https://github.com/nix-community/vscode-nix-ide/issues/424)) ([7038804](https://github.com/nix-community/vscode-nix-ide/commit/7038804b9e06d04df254fc4b516363a53a7f063e)) 183 | 184 | ### [0.3.5](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.4...v0.3.5) (2024-09-13) 185 | 186 | 187 | ### Bug Fixes 188 | 189 | * reload language server client on restart ([#420](https://github.com/nix-community/vscode-nix-ide/issues/420)) ([4c48cb0](https://github.com/nix-community/vscode-nix-ide/commit/4c48cb06cddf439f65d6ec066f41c6a6432ffa5a)), closes [#419](https://github.com/nix-community/vscode-nix-ide/issues/419) 190 | 191 | ### [0.3.4](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.3...v0.3.4) (2024-09-10) 192 | 193 | 194 | ### Features 195 | 196 | * add setting to suppress error notifications ([4913819](https://github.com/nix-community/vscode-nix-ide/commit/4913819384f2fe70e15ea7c814a739926a5d1280)) 197 | 198 | ### [0.3.3](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.2...v0.3.3) (2024-08-02) 199 | 200 | 201 | ### Bug Fixes 202 | 203 | * vsce latest ([cecea30](https://github.com/nix-community/vscode-nix-ide/commit/cecea3041cfdb0aebc6c7e6c02dfeca193c360d6)) 204 | 205 | ### [0.3.2](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.1...v0.3.2) (2024-08-02) 206 | 207 | 208 | ### Features 209 | 210 | * add restarting language server command ([#381](https://github.com/nix-community/vscode-nix-ide/issues/381)) ([c52dc29](https://github.com/nix-community/vscode-nix-ide/commit/c52dc292a7d3b356bc1a18d31f9df8395770d679)) 211 | * migrate eslint config ([5fd26eb](https://github.com/nix-community/vscode-nix-ide/commit/5fd26eb00f6c3c9fabbf9fce4c43b6259fa6d1ed)) 212 | * support use vscode variables in settings ([#399](https://github.com/nix-community/vscode-nix-ide/issues/399)) ([a602050](https://github.com/nix-community/vscode-nix-ide/commit/a6020509005221a0bb885b2d5914fd8f4563f97d)) 213 | 214 | 215 | ### Bug Fixes 216 | 217 | * dependabot config ([df48657](https://github.com/nix-community/vscode-nix-ide/commit/df4865707169754900a2f609eacd38f82586b8ed)) 218 | * eslint errors ([de74fc2](https://github.com/nix-community/vscode-nix-ide/commit/de74fc218d73fbb22fe97708a666798dee35335f)) 219 | 220 | ### [0.3.1](https://github.com/nix-community/vscode-nix-ide/compare/v0.3.0...v0.3.1) (2024-03-12) 221 | 222 | ## [0.3.0](https://github.com/nix-community/vscode-nix-ide/compare/v0.2.2...v0.3.0) (2024-03-12) 223 | 224 | 225 | ### ⚠ BREAKING CHANGES 226 | 227 | * upgrade dependencies 228 | 229 | * upgrade dependencies ([3778ed6](https://github.com/nix-community/vscode-nix-ide/commit/3778ed648f1a0f73b8826860f591ab7b9e9bb355)) 230 | 231 | ### [0.2.2](https://github.com/nix-community/vscode-nix-ide/compare/v0.2.1...v0.2.2) (2023-07-26) 232 | 233 | 234 | ### Features 235 | 236 | * set file icon ([f7c2244](https://github.com/nix-community/vscode-nix-ide/commit/f7c22449ca7de99a6421a9694c606486eee607a8)) 237 | 238 | ### [0.2.1](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.23...v0.2.1) (2022-10-14) 239 | 240 | 241 | ### Features 242 | 243 | * declare support for nil ([#276](https://github.com/nix-community/vscode-nix-ide/issues/276)) ([c19ceba](https://github.com/nix-community/vscode-nix-ide/commit/c19ceba897f4e23aba6bad543a16ee901f4195f6)) 244 | * passing settings to lsp ([#294](https://github.com/nix-community/vscode-nix-ide/issues/294)) ([c898347](https://github.com/nix-community/vscode-nix-ide/commit/c89834791c8eb8230cd5d07d3754bdb30ed8b822)) 245 | 246 | ### [0.1.24](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.23...v0.1.24) (2022-10-14) 247 | 248 | ### [0.1.23](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.22...v0.1.23) (2022-08-16) 249 | 250 | 251 | ### Features 252 | 253 | * add nix-shell and direnv support ([#251](https://github.com/nix-community/vscode-nix-ide/issues/251)) ([d78b62d](https://github.com/nix-community/vscode-nix-ide/commit/d78b62ded8e575c01922ff781884af026578c7a4)) 254 | 255 | 256 | ### Bug Fixes 257 | 258 | * catch error if serverPath doesn't exist ([#267](https://github.com/nix-community/vscode-nix-ide/issues/267)) ([95caf81](https://github.com/nix-community/vscode-nix-ide/commit/95caf810a2f0415103ebc5c3dfde7b78729be4c3)) 259 | 260 | ### [0.1.22](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.20...v0.1.22) (2022-08-14) 261 | 262 | 263 | ### Bug Fixes 264 | 265 | * mark interpolation as meta.embedded instead of markup.italic ([cd420d0](https://github.com/nix-community/vscode-nix-ide/commit/cd420d0bcea26cf1cf650f47c738bd1b6658a80c)) 266 | 267 | ### [0.1.21](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.20...v0.1.21) (2022-08-14) 268 | 269 | 270 | ### Bug Fixes 271 | 272 | * mark interpolation as meta.embedded instead of markup.italic ([cd420d0](https://github.com/nix-community/vscode-nix-ide/commit/cd420d0bcea26cf1cf650f47c738bd1b6658a80c)) 273 | 274 | ### [0.1.20](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.19...v0.1.20) (2022-02-23) 275 | 276 | ### [0.1.19](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.18...v0.1.19) (2022-01-02) 277 | 278 | 279 | ### Features 280 | 281 | * antiquotation brackets ([55e9258](https://github.com/nix-community/vscode-nix-ide/commit/55e92589b9b73eda3314a7b14e059c0ff77a1bfb)) 282 | * path angle brackets ([f79b542](https://github.com/nix-community/vscode-nix-ide/commit/f79b54226f2ad5a6ddbe968c632c4c75b2141974)) 283 | * word pattern ([d1389f6](https://github.com/nix-community/vscode-nix-ide/commit/d1389f6c479ceee7813635f793d7d3c02e27329c)) 284 | 285 | 286 | ### Bug Fixes 287 | 288 | * auto close at end of string ([ae0c981](https://github.com/nix-community/vscode-nix-ide/commit/ae0c981de0a60a5910dca472643bbbf43ef51c63)) 289 | * auto close double quotes in comments ([aab9cef](https://github.com/nix-community/vscode-nix-ide/commit/aab9ceffa3e52a4938b5907697b7edadd51069fb)) 290 | * don't auto close single quotes ([c912fc8](https://github.com/nix-community/vscode-nix-ide/commit/c912fc8c128c0bc1c4600ae2aa904237cb13e780)) 291 | * folding marker comments ([bab9fdf](https://github.com/nix-community/vscode-nix-ide/commit/bab9fdfcbb2ae2fcceb8c11cec2c1e96d6f14c45)) 292 | * quotes inside attributes ([b003401](https://github.com/nix-community/vscode-nix-ide/commit/b0034014a1b96ac862e4ff678e2f93917fdd7f91)), closes [#189](https://github.com/nix-community/vscode-nix-ide/issues/189) 293 | * update branch names in ci configs ([66e6991](https://github.com/nix-community/vscode-nix-ide/commit/66e69919cf84fd4465378368a8525a935ce9c81a)) 294 | 295 | ### [0.1.18](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.17...v0.1.18) (2021-10-12) 296 | 297 | ### [0.1.17](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.16...v0.1.17) (2021-10-12) 298 | 299 | 300 | ### Features 301 | 302 | * make the formatter path configurable ([03c25cb](https://github.com/nix-community/vscode-nix-ide/commit/03c25cb57dc36cf23dd2641abe3d34fbec01eb4a)) 303 | 304 | 305 | ### Bug Fixes 306 | 307 | * release to ovsx registry ([b07688e](https://github.com/nix-community/vscode-nix-ide/commit/b07688ec19d97337be8e8b1371e911b6b908884a)) 308 | 309 | ### [0.1.16](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.15...v0.1.16) (2021-08-20) 310 | 311 | 312 | ### Features 313 | 314 | * add esbuild bundling ([2ee7a3f](https://github.com/nix-community/vscode-nix-ide/commit/2ee7a3f99e25c895fa39be3cde210d7a748d4bee)) 315 | 316 | ### [0.1.15](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.14...v0.1.15) (2021-08-16) 317 | 318 | ### [0.1.14](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.13...v0.1.14) (2021-08-16) 319 | 320 | 321 | ### Features 322 | 323 | * upgrade dependencies to latest versions ([072b823](https://github.com/nix-community/vscode-nix-ide/commit/072b823d1f8fa6a55a870a94233e44af6ee14e57)) 324 | 325 | ### [0.1.13](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.12...v0.1.13) (2021-08-16) 326 | 327 | ### [0.1.12](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.11...v0.1.12) (2021-05-08) 328 | 329 | ### [0.1.11](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.10...v0.1.11) (2021-05-08) 330 | 331 | 332 | ### Features 333 | 334 | * **linter:** Allow symbol errors to show (static analysys) ([041dffc](https://github.com/nix-community/vscode-nix-ide/commit/041dffc92fae798c41ed211ee805ee6ce5772c5f)) 335 | 336 | ### [0.1.10](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.9...v0.1.10) (2021-03-24) 337 | 338 | ### [0.1.9](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.8...v0.1.9) (2021-03-24) 339 | 340 | ### [0.1.8](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.7...v0.1.8) (2020-12-23) 341 | 342 | 343 | ### Features 344 | 345 | * create vsix artifacts part of release ([6e2c1fe](https://github.com/nix-community/vscode-nix-ide/commit/6e2c1fe962744936669b47a5b42b57f6d19b304c)) 346 | 347 | ### [0.1.7](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.6...v0.1.7) (2020-12-23) 348 | 349 | ### [0.1.6](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.5...v0.1.6) (2020-12-23) 350 | 351 | 352 | ### Features 353 | 354 | * add rnix LSP support ([ec3dee3](https://github.com/nix-community/vscode-nix-ide/commit/ec3dee34ba273d181593e647f305de10d54e5e10)), closes [#10](https://github.com/nix-community/vscode-nix-ide/issues/10) 355 | * check rnix-lsp and suggest if not installed ([e09563f](https://github.com/nix-community/vscode-nix-ide/commit/e09563f849fd7cb9d28c688a1c0c3bd2da890041)) 356 | 357 | 358 | ### Bug Fixes 359 | 360 | * since update to lsp-client v7 ([e3fa686](https://github.com/nix-community/vscode-nix-ide/commit/e3fa686464dcac5319ddae5a5d23904c4f5bc487)) 361 | 362 | ### [0.1.5](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.3...v0.1.5) (2020-10-24) 363 | 364 | ### [0.1.3](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.2...v0.1.3) (2020-08-16) 365 | 366 | ### [0.1.2](https://github.com/nix-community/vscode-nix-ide/compare/v0.1.1...v0.1.2) (2020-08-16) 367 | 368 | ### 0.1.1 (2020-08-16) 369 | 370 | 371 | ### Features 372 | 373 | * add fable to project ([e7b4962](https://github.com/nix-community/vscode-nix-ide/commit/e7b49622ed06a2ad7d9bb7d69ed5a1ef6d0af9e5)) 374 | * add license info ([f559fc3](https://github.com/nix-community/vscode-nix-ide/commit/f559fc3cd41980d698fb62a18c5f4b636d32c403)) 375 | * add linter for nix files with nix-instantiate ([cbf6abe](https://github.com/nix-community/vscode-nix-ide/commit/cbf6abef33a948bdbc5c9c54019e93d0320ba965)) 376 | * adding basic snippets ([02e1995](https://github.com/nix-community/vscode-nix-ide/commit/02e199541e6db1f74c6a2de680957f81a9afa498)) 377 | * initial commit ([35d49df](https://github.com/nix-community/vscode-nix-ide/commit/35d49df84975c0f3940173f0be517f5bc94528b3)) 378 | * markdown embedded support ([4debbfd](https://github.com/nix-community/vscode-nix-ide/commit/4debbfd90884930c5e7f5d49141fd5017dd23d56)) 379 | * update language configuration ([188c9cf](https://github.com/nix-community/vscode-nix-ide/commit/188c9cfecbcf5b8cb73f1c98ba9435eb68c394b6)) 380 | 381 | 382 | ### Bug Fixes 383 | 384 | * syntax file name ([3113b30](https://github.com/nix-community/vscode-nix-ide/commit/3113b3073f1ec62bed8a4eddc9cb7dab994b97c9)) 385 | 386 | ### 0.0.2 (2020-08-16) 387 | 388 | 389 | ### Features 390 | 391 | * add fable to project ([e7b4962](https://github.com/nix-community/vscode-nix-ide/commit/e7b49622ed06a2ad7d9bb7d69ed5a1ef6d0af9e5)) 392 | * add license info ([f559fc3](https://github.com/nix-community/vscode-nix-ide/commit/f559fc3cd41980d698fb62a18c5f4b636d32c403)) 393 | * add linter for nix files with nix-instantiate ([cbf6abe](https://github.com/nix-community/vscode-nix-ide/commit/cbf6abef33a948bdbc5c9c54019e93d0320ba965)) 394 | * adding basic snippets ([02e1995](https://github.com/nix-community/vscode-nix-ide/commit/02e199541e6db1f74c6a2de680957f81a9afa498)) 395 | * initial commit ([35d49df](https://github.com/nix-community/vscode-nix-ide/commit/35d49df84975c0f3940173f0be517f5bc94528b3)) 396 | * markdown embedded support ([4debbfd](https://github.com/nix-community/vscode-nix-ide/commit/4debbfd90884930c5e7f5d49141fd5017dd23d56)) 397 | * update language configuration ([188c9cf](https://github.com/nix-community/vscode-nix-ide/commit/188c9cfecbcf5b8cb73f1c98ba9435eb68c394b6)) 398 | 399 | 400 | ### Bug Fixes 401 | 402 | * syntax file name ([3113b30](https://github.com/nix-community/vscode-nix-ide/commit/3113b3073f1ec62bed8a4eddc9cb7dab994b97c9)) 403 | 404 | # Change Log 405 | 406 | All notable changes to the "nix" extension will be documented in this file. 407 | 408 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 409 | 410 | ## [Unreleased] 411 | 412 | - Initial release 413 | -------------------------------------------------------------------------------- /dist/nix.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nix", 3 | "scopeName": "source.nix", 4 | "fileTypes": [ 5 | "nix" 6 | ], 7 | "uuid": "0514fd5f-acb6-436d-b42c-7643e6d36c8f", 8 | "patterns": [ 9 | { 10 | "include": "#expression" 11 | } 12 | ], 13 | "repository": { 14 | "expression": { 15 | "patterns": [ 16 | { 17 | "include": "#parens-and-cont" 18 | }, 19 | { 20 | "include": "#list-and-cont" 21 | }, 22 | { 23 | "include": "#string" 24 | }, 25 | { 26 | "include": "#interpolation" 27 | }, 28 | { 29 | "include": "#with-assert" 30 | }, 31 | { 32 | "include": "#function-for-sure" 33 | }, 34 | { 35 | "include": "#attrset-for-sure" 36 | }, 37 | { 38 | "include": "#attrset-or-function" 39 | }, 40 | { 41 | "include": "#let" 42 | }, 43 | { 44 | "include": "#if" 45 | }, 46 | { 47 | "include": "#operator-unary" 48 | }, 49 | { 50 | "include": "#operator-binary" 51 | }, 52 | { 53 | "include": "#constants" 54 | }, 55 | { 56 | "include": "#bad-reserved" 57 | }, 58 | { 59 | "include": "#parameter-name-and-cont" 60 | }, 61 | { 62 | "include": "#others" 63 | } 64 | ] 65 | }, 66 | "expression-cont": { 67 | "begin": "(?=.?)", 68 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 69 | "patterns": [ 70 | { 71 | "include": "#parens" 72 | }, 73 | { 74 | "include": "#list" 75 | }, 76 | { 77 | "include": "#string" 78 | }, 79 | { 80 | "include": "#interpolation" 81 | }, 82 | { 83 | "include": "#function-for-sure" 84 | }, 85 | { 86 | "include": "#attrset-for-sure" 87 | }, 88 | { 89 | "include": "#attrset-or-function" 90 | }, 91 | { 92 | "include": "#operator-binary" 93 | }, 94 | { 95 | "include": "#constants" 96 | }, 97 | { 98 | "include": "#bad-reserved" 99 | }, 100 | { 101 | "include": "#parameter-name" 102 | }, 103 | { 104 | "include": "#others" 105 | } 106 | ] 107 | }, 108 | "parens": { 109 | "begin": "\\(", 110 | "beginCaptures": { 111 | "0": { 112 | "name": "punctuation.definition.expression.nix" 113 | } 114 | }, 115 | "end": "\\)", 116 | "endCaptures": { 117 | "0": { 118 | "name": "punctuation.definition.expression.nix" 119 | } 120 | }, 121 | "patterns": [ 122 | { 123 | "include": "#expression" 124 | } 125 | ] 126 | }, 127 | "parens-and-cont": { 128 | "begin": "(?=\\()", 129 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 130 | "patterns": [ 131 | { 132 | "include": "#parens" 133 | }, 134 | { 135 | "include": "#expression-cont" 136 | } 137 | ] 138 | }, 139 | "list": { 140 | "begin": "\\[", 141 | "beginCaptures": { 142 | "0": { 143 | "name": "punctuation.definition.list.nix" 144 | } 145 | }, 146 | "end": "\\]", 147 | "endCaptures": { 148 | "0": { 149 | "name": "punctuation.definition.list.nix" 150 | } 151 | }, 152 | "patterns": [ 153 | { 154 | "include": "#expression" 155 | } 156 | ] 157 | }, 158 | "list-and-cont": { 159 | "begin": "(?=\\[)", 160 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 161 | "patterns": [ 162 | { 163 | "include": "#list" 164 | }, 165 | { 166 | "include": "#expression-cont" 167 | } 168 | ] 169 | }, 170 | "attrset-for-sure": { 171 | "patterns": [ 172 | { 173 | "begin": "(?=\\brec\\b)", 174 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 175 | "patterns": [ 176 | { 177 | "begin": "\\brec\\b", 178 | "end": "(?=\\{)", 179 | "beginCaptures": { 180 | "0": { 181 | "name": "keyword.other.nix" 182 | } 183 | }, 184 | "patterns": [ 185 | { 186 | "include": "#others" 187 | } 188 | ] 189 | }, 190 | { 191 | "include": "#attrset-definition" 192 | }, 193 | { 194 | "include": "#others" 195 | } 196 | ] 197 | }, 198 | { 199 | "begin": "(?=\\{\\s*(\\}|[^,?]*(=|;)))", 200 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 201 | "patterns": [ 202 | { 203 | "include": "#attrset-definition" 204 | }, 205 | { 206 | "include": "#others" 207 | } 208 | ] 209 | } 210 | ] 211 | }, 212 | "attrset-definition": { 213 | "begin": "(?=\\{)", 214 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 215 | "patterns": [ 216 | { 217 | "begin": "(\\{)", 218 | "end": "(\\})", 219 | "beginCaptures": { 220 | "0": { 221 | "name": "punctuation.definition.attrset.nix" 222 | } 223 | }, 224 | "endCaptures": { 225 | "0": { 226 | "name": "punctuation.definition.attrset.nix" 227 | } 228 | }, 229 | "patterns": [ 230 | { 231 | "include": "#attrset-contents" 232 | } 233 | ] 234 | }, 235 | { 236 | "begin": "(?<=\\})", 237 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 238 | "patterns": [ 239 | { 240 | "include": "#expression-cont" 241 | } 242 | ] 243 | } 244 | ] 245 | }, 246 | "attrset-definition-brace-opened": { 247 | "patterns": [ 248 | { 249 | "begin": "(?<=\\})", 250 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 251 | "patterns": [ 252 | { 253 | "include": "#expression-cont" 254 | } 255 | ] 256 | }, 257 | { 258 | "begin": "(?=.?)", 259 | "end": "\\}", 260 | "endCaptures": { 261 | "0": { 262 | "name": "punctuation.definition.attrset.nix" 263 | } 264 | }, 265 | "patterns": [ 266 | { 267 | "include": "#attrset-contents" 268 | } 269 | ] 270 | } 271 | ] 272 | }, 273 | "attrset-contents": { 274 | "patterns": [ 275 | { 276 | "include": "#attribute-inherit" 277 | }, 278 | { 279 | "include": "#bad-reserved" 280 | }, 281 | { 282 | "include": "#attribute-bind" 283 | }, 284 | { 285 | "include": "#others" 286 | } 287 | ] 288 | }, 289 | "function-header-open-brace": { 290 | "begin": "\\{", 291 | "end": "(?=\\})", 292 | "beginCaptures": { 293 | "0": { 294 | "name": "punctuation.definition.entity.function.2.nix" 295 | } 296 | }, 297 | "patterns": [ 298 | { 299 | "include": "#function-contents" 300 | } 301 | ] 302 | }, 303 | "function-header-close-brace-no-arg": { 304 | "begin": "\\}", 305 | "end": "(?=\\:)", 306 | "beginCaptures": { 307 | "0": { 308 | "name": "punctuation.definition.entity.function.nix" 309 | } 310 | }, 311 | "patterns": [ 312 | { 313 | "include": "#others" 314 | } 315 | ] 316 | }, 317 | "function-header-terminal-arg": { 318 | "begin": "(?=@)", 319 | "end": "(?=\\:)", 320 | "patterns": [ 321 | { 322 | "begin": "\\@", 323 | "end": "(?=\\:)", 324 | "patterns": [ 325 | { 326 | "begin": "(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*)", 327 | "end": "(?=\\:)", 328 | "name": "variable.parameter.function.3.nix" 329 | }, 330 | { 331 | "include": "#others" 332 | } 333 | ] 334 | }, 335 | { 336 | "include": "#others" 337 | } 338 | ] 339 | }, 340 | "function-header-close-brace-with-arg": { 341 | "begin": "\\}", 342 | "end": "(?=\\:)", 343 | "beginCaptures": { 344 | "0": { 345 | "name": "punctuation.definition.entity.function.nix" 346 | } 347 | }, 348 | "patterns": [ 349 | { 350 | "include": "#function-header-terminal-arg" 351 | }, 352 | { 353 | "include": "#others" 354 | } 355 | ] 356 | }, 357 | "function-header-until-colon-no-arg": { 358 | "begin": "(?=\\{)", 359 | "end": "(?=\\:)", 360 | "patterns": [ 361 | { 362 | "include": "#function-header-open-brace" 363 | }, 364 | { 365 | "include": "#function-header-close-brace-no-arg" 366 | } 367 | ] 368 | }, 369 | "function-header-until-colon-with-arg": { 370 | "begin": "(?=\\{)", 371 | "end": "(?=\\:)", 372 | "patterns": [ 373 | { 374 | "include": "#function-header-open-brace" 375 | }, 376 | { 377 | "include": "#function-header-close-brace-with-arg" 378 | } 379 | ] 380 | }, 381 | "function-body-from-colon": { 382 | "begin": "(\\:)", 383 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 384 | "beginCaptures": { 385 | "0": { 386 | "name": "punctuation.definition.function.nix" 387 | } 388 | }, 389 | "patterns": [ 390 | { 391 | "include": "#expression" 392 | } 393 | ] 394 | }, 395 | "function-definition": { 396 | "begin": "(?=.?)", 397 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 398 | "patterns": [ 399 | { 400 | "include": "#function-body-from-colon" 401 | }, 402 | { 403 | "begin": "(?=.?)", 404 | "end": "(?=\\:)", 405 | "patterns": [ 406 | { 407 | "begin": "(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*)", 408 | "end": "(?=\\:)", 409 | "beginCaptures": { 410 | "0": { 411 | "name": "variable.parameter.function.4.nix" 412 | } 413 | }, 414 | "patterns": [ 415 | { 416 | "begin": "\\@", 417 | "end": "(?=\\:)", 418 | "patterns": [ 419 | { 420 | "include": "#function-header-until-colon-no-arg" 421 | }, 422 | { 423 | "include": "#others" 424 | } 425 | ] 426 | }, 427 | { 428 | "include": "#others" 429 | } 430 | ] 431 | }, 432 | { 433 | "begin": "(?=\\{)", 434 | "end": "(?=\\:)", 435 | "patterns": [ 436 | { 437 | "include": "#function-header-until-colon-with-arg" 438 | } 439 | ] 440 | } 441 | ] 442 | }, 443 | { 444 | "include": "#others" 445 | } 446 | ] 447 | }, 448 | "function-definition-brace-opened": { 449 | "begin": "(?=.?)", 450 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 451 | "patterns": [ 452 | { 453 | "include": "#function-body-from-colon" 454 | }, 455 | { 456 | "begin": "(?=.?)", 457 | "end": "(?=\\:)", 458 | "patterns": [ 459 | { 460 | "include": "#function-header-close-brace-with-arg" 461 | }, 462 | { 463 | "begin": "(?=.?)", 464 | "end": "(?=\\})", 465 | "patterns": [ 466 | { 467 | "include": "#function-contents" 468 | } 469 | ] 470 | } 471 | ] 472 | }, 473 | { 474 | "include": "#others" 475 | } 476 | ] 477 | }, 478 | "function-for-sure": { 479 | "patterns": [ 480 | { 481 | "begin": "(?=(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*\\s*[:@]|\\{[^}\\\"']*\\}\\s*:|\\{[^#}\"'/=]*[,\\?]))", 482 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 483 | "patterns": [ 484 | { 485 | "include": "#function-definition" 486 | } 487 | ] 488 | } 489 | ] 490 | }, 491 | "function-contents": { 492 | "patterns": [ 493 | { 494 | "include": "#bad-reserved" 495 | }, 496 | { 497 | "include": "#function-parameter" 498 | }, 499 | { 500 | "include": "#others" 501 | } 502 | ] 503 | }, 504 | "attrset-or-function": { 505 | "begin": "\\{", 506 | "beginCaptures": { 507 | "0": { 508 | "name": "punctuation.definition.attrset-or-function.nix" 509 | } 510 | }, 511 | "end": "(?=([\\])};]|\\b(else|then)\\b))", 512 | "patterns": [ 513 | { 514 | "begin": "(?=(\\s*\\}|\\\"|\\binherit\\b|\\$\\{|\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*(\\s*\\.|\\s*=[^=])))", 515 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 516 | "patterns": [ 517 | { 518 | "include": "#attrset-definition-brace-opened" 519 | } 520 | ] 521 | }, 522 | { 523 | "begin": "(?=(\\.\\.\\.|\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*\\s*[,?]))", 524 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 525 | "patterns": [ 526 | { 527 | "include": "#function-definition-brace-opened" 528 | } 529 | ] 530 | }, 531 | { 532 | "include": "#bad-reserved" 533 | }, 534 | { 535 | "begin": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 536 | "end": "(?=([\\])};]|\\b(else|then)\\b))", 537 | "beginCaptures": { 538 | "0": { 539 | "name": "variable.parameter.function.maybe.nix" 540 | } 541 | }, 542 | "patterns": [ 543 | { 544 | "begin": "(?=\\.)", 545 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 546 | "patterns": [ 547 | { 548 | "include": "#attrset-definition-brace-opened" 549 | } 550 | ] 551 | }, 552 | { 553 | "begin": "\\s*(\\,)", 554 | "beginCaptures": { 555 | "1": { 556 | "name": "keyword.operator.nix" 557 | } 558 | }, 559 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 560 | "patterns": [ 561 | { 562 | "include": "#function-definition-brace-opened" 563 | } 564 | ] 565 | }, 566 | { 567 | "begin": "(?=\\=)", 568 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 569 | "patterns": [ 570 | { 571 | "include": "#attribute-bind-from-equals" 572 | }, 573 | { 574 | "include": "#attrset-definition-brace-opened" 575 | } 576 | ] 577 | }, 578 | { 579 | "begin": "(?=\\?)", 580 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 581 | "patterns": [ 582 | { 583 | "include": "#function-parameter-default" 584 | }, 585 | { 586 | "begin": "\\,", 587 | "beginCaptures": { 588 | "0": { 589 | "name": "keyword.operator.nix" 590 | } 591 | }, 592 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 593 | "patterns": [ 594 | { 595 | "include": "#function-definition-brace-opened" 596 | } 597 | ] 598 | } 599 | ] 600 | }, 601 | { 602 | "include": "#others" 603 | } 604 | ] 605 | }, 606 | { 607 | "include": "#others" 608 | } 609 | ] 610 | }, 611 | "with-assert": { 612 | "begin": "(?)", 882 | "beginCaptures": { 883 | "0": { 884 | "name": "string.unquoted.spath.nix" 885 | } 886 | }, 887 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 888 | "patterns": [ 889 | { 890 | "include": "#expression-cont" 891 | } 892 | ] 893 | }, 894 | { 895 | "begin": "([a-zA-Z][a-zA-Z0-9\\+\\-\\.]*\\:[a-zA-Z0-9\\%\\/\\?\\:\\@\\&\\=\\+\\$\\,\\-\\_\\.\\!\\~\\*\\']+)", 896 | "beginCaptures": { 897 | "0": { 898 | "name": "string.unquoted.url.nix" 899 | } 900 | }, 901 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 902 | "patterns": [ 903 | { 904 | "include": "#expression-cont" 905 | } 906 | ] 907 | } 908 | ] 909 | }, 910 | "parameter-name": { 911 | "match": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 912 | "captures": { 913 | "0": { 914 | "name": "variable.parameter.name.nix" 915 | } 916 | } 917 | }, 918 | "parameter-name-and-cont": { 919 | "begin": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 920 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 921 | "beginCaptures": { 922 | "0": { 923 | "name": "variable.parameter.name.nix" 924 | } 925 | }, 926 | "patterns": [ 927 | { 928 | "include": "#expression-cont" 929 | } 930 | ] 931 | }, 932 | "attribute-name-single": { 933 | "match": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 934 | "name": "entity.other.attribute-name.single.nix" 935 | }, 936 | "attribute-name": { 937 | "patterns": [ 938 | { 939 | "match": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 940 | "name": "entity.other.attribute-name.multipart.nix" 941 | }, 942 | { 943 | "match": "\\." 944 | }, 945 | { 946 | "include": "#string-quoted" 947 | }, 948 | { 949 | "include": "#interpolation" 950 | } 951 | ] 952 | }, 953 | "function-parameter-default": { 954 | "begin": "\\?", 955 | "beginCaptures": { 956 | "0": { 957 | "name": "keyword.operator.nix" 958 | } 959 | }, 960 | "end": "(?=[,}])", 961 | "patterns": [ 962 | { 963 | "include": "#expression" 964 | } 965 | ] 966 | }, 967 | "function-parameter": { 968 | "patterns": [ 969 | { 970 | "begin": "(\\.\\.\\.)", 971 | "end": "(,|(?=\\}))", 972 | "name": "keyword.operator.nix", 973 | "patterns": [ 974 | { 975 | "include": "#others" 976 | } 977 | ] 978 | }, 979 | { 980 | "begin": "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 981 | "beginCaptures": { 982 | "0": { 983 | "name": "variable.parameter.function.1.nix" 984 | } 985 | }, 986 | "end": "(,|(?=\\}))", 987 | "endCaptures": { 988 | "0": { 989 | "name": "keyword.operator.nix" 990 | } 991 | }, 992 | "patterns": [ 993 | { 994 | "include": "#whitespace" 995 | }, 996 | { 997 | "include": "#comment" 998 | }, 999 | { 1000 | "include": "#function-parameter-default" 1001 | }, 1002 | { 1003 | "include": "#expression" 1004 | } 1005 | ] 1006 | }, 1007 | { 1008 | "include": "#others" 1009 | } 1010 | ] 1011 | }, 1012 | "attribute-inherit": { 1013 | "begin": "\\binherit\\b", 1014 | "beginCaptures": { 1015 | "0": { 1016 | "name": "keyword.other.inherit.nix" 1017 | } 1018 | }, 1019 | "end": "\\;", 1020 | "endCaptures": { 1021 | "0": { 1022 | "name": "punctuation.terminator.inherit.nix" 1023 | } 1024 | }, 1025 | "patterns": [ 1026 | { 1027 | "begin": "\\(", 1028 | "end": "(?=\\;)", 1029 | "beginCaptures": { 1030 | "0": { 1031 | "name": "punctuation.section.function.arguments.nix" 1032 | } 1033 | }, 1034 | "patterns": [ 1035 | { 1036 | "begin": "\\)", 1037 | "end": "(?=\\;)", 1038 | "beginCaptures": { 1039 | "0": { 1040 | "name": "punctuation.section.function.arguments.nix" 1041 | } 1042 | }, 1043 | "patterns": [ 1044 | { 1045 | "include": "#bad-reserved" 1046 | }, 1047 | { 1048 | "include": "#attribute-name-single" 1049 | }, 1050 | { 1051 | "include": "#others" 1052 | } 1053 | ] 1054 | }, 1055 | { 1056 | "include": "#expression" 1057 | } 1058 | ] 1059 | }, 1060 | { 1061 | "begin": "(?=[a-zA-Z\\_])", 1062 | "end": "(?=\\;)", 1063 | "patterns": [ 1064 | { 1065 | "include": "#bad-reserved" 1066 | }, 1067 | { 1068 | "include": "#attribute-name-single" 1069 | }, 1070 | { 1071 | "include": "#others" 1072 | } 1073 | ] 1074 | }, 1075 | { 1076 | "include": "#others" 1077 | } 1078 | ] 1079 | }, 1080 | "attribute-bind-from-equals": { 1081 | "begin": "\\=", 1082 | "beginCaptures": { 1083 | "0": { 1084 | "name": "keyword.operator.bind.nix" 1085 | } 1086 | }, 1087 | "end": "\\;", 1088 | "endCaptures": { 1089 | "0": { 1090 | "name": "punctuation.terminator.bind.nix" 1091 | } 1092 | }, 1093 | "patterns": [ 1094 | { 1095 | "include": "#expression" 1096 | } 1097 | ] 1098 | }, 1099 | "attribute-bind": { 1100 | "patterns": [ 1101 | { 1102 | "include": "#attribute-name" 1103 | }, 1104 | { 1105 | "include": "#attribute-bind-from-equals" 1106 | } 1107 | ] 1108 | }, 1109 | "operator-unary": { 1110 | "name": "keyword.operator.unary.nix", 1111 | "match": "(!|-)" 1112 | }, 1113 | "operator-binary": { 1114 | "name": "keyword.operator.nix", 1115 | "match": "(\\bor\\b|\\.|\\|\\>|\\<\\||==|!=|!|\\<\\=|\\<|\\>\\=|\\>|&&|\\|\\||-\\>|//|\\?|\\+\\+|-|\\*|/(?=([^*]|$))|\\+)" 1116 | }, 1117 | "constants": { 1118 | "patterns": [ 1119 | { 1120 | "begin": "\\b(builtins|true|false|null)\\b", 1121 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 1122 | "beginCaptures": { 1123 | "0": { 1124 | "name": "constant.language.nix" 1125 | } 1126 | }, 1127 | "patterns": [ 1128 | { 1129 | "include": "#expression-cont" 1130 | } 1131 | ] 1132 | }, 1133 | { 1134 | "beginCaptures": { 1135 | "0": { 1136 | "name": "support.function.nix" 1137 | } 1138 | }, 1139 | "begin": "\\b(scopedImport|import|isNull|abort|throw|baseNameOf|dirOf|removeAttrs|map|toString|derivationStrict|derivation)\\b", 1140 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 1141 | "patterns": [ 1142 | { 1143 | "include": "#expression-cont" 1144 | } 1145 | ] 1146 | }, 1147 | { 1148 | "beginCaptures": { 1149 | "0": { 1150 | "name": "constant.numeric.nix" 1151 | } 1152 | }, 1153 | "begin": "\\b[0-9]+\\b", 1154 | "end": "(?=([\\])};,]|\\b(else|then)\\b))", 1155 | "patterns": [ 1156 | { 1157 | "include": "#expression-cont" 1158 | } 1159 | ] 1160 | } 1161 | ] 1162 | }, 1163 | "whitespace": { 1164 | "match": "\\s+" 1165 | }, 1166 | "illegal": { 1167 | "match": ".", 1168 | "name": "invalid.illegal" 1169 | }, 1170 | "others": { 1171 | "patterns": [ 1172 | { 1173 | "include": "#whitespace" 1174 | }, 1175 | { 1176 | "include": "#comment" 1177 | }, 1178 | { 1179 | "include": "#illegal" 1180 | } 1181 | ] 1182 | }, 1183 | "bad-reserved": { 1184 | "match": "(?(target: T) { 38 | // // Convert PascalCase to kebab-case (e.g., WhiteSpace -> white-space) 39 | // const kebabCase = target.name 40 | // .replace(/([a-z])([A-Z])/g, '$1-$2') 41 | // .toLowerCase(); 42 | // Object.defineProperty(target, 'id', {value: `#${kebabCase}`}); 43 | // return target; 44 | // } 45 | 46 | export const source_nix = { 47 | name: "Nix", 48 | scopeName: "source.nix", 49 | fileTypes: ["nix"], 50 | uuid: "0514fd5f-acb6-436d-b42c-7643e6d36c8f", 51 | patterns: [ 52 | { 53 | include: "#expression", 54 | }, 55 | ], 56 | repository: { 57 | expression: { 58 | patterns: [ 59 | { 60 | include: "#parens-and-cont", 61 | }, 62 | { 63 | include: "#list-and-cont", 64 | }, 65 | { 66 | include: "#string", 67 | }, 68 | { 69 | include: "#interpolation", 70 | }, 71 | { 72 | include: "#with-assert", 73 | }, 74 | { 75 | include: "#function-for-sure", 76 | }, 77 | { 78 | include: "#attrset-for-sure", 79 | }, 80 | { 81 | include: "#attrset-or-function", 82 | }, 83 | { 84 | include: "#let", 85 | }, 86 | { 87 | include: "#if", 88 | }, 89 | { 90 | include: "#operator-unary", 91 | }, 92 | { 93 | include: "#operator-binary", 94 | }, 95 | { 96 | include: "#constants", 97 | }, 98 | { 99 | include: "#bad-reserved", 100 | }, 101 | { 102 | include: "#parameter-name-and-cont", 103 | }, 104 | { 105 | include: "#others", 106 | }, 107 | ], 108 | }, 109 | "expression-cont": { 110 | begin: "(?=.?)", 111 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 112 | patterns: [ 113 | { 114 | include: "#parens", 115 | }, 116 | { 117 | include: "#list", 118 | }, 119 | { 120 | include: "#string", 121 | }, 122 | { 123 | include: "#interpolation", 124 | }, 125 | { 126 | include: "#function-for-sure", 127 | }, 128 | { 129 | include: "#attrset-for-sure", 130 | }, 131 | { 132 | include: "#attrset-or-function", 133 | }, 134 | { 135 | include: "#operator-binary", 136 | }, 137 | { 138 | include: "#constants", 139 | }, 140 | { 141 | include: "#bad-reserved", 142 | }, 143 | { 144 | include: "#parameter-name", 145 | }, 146 | { 147 | include: "#others", 148 | }, 149 | ], 150 | }, 151 | parens: { 152 | begin: "\\(", 153 | beginCaptures: { 154 | "0": { 155 | name: "punctuation.definition.expression.nix", 156 | }, 157 | }, 158 | end: "\\)", 159 | endCaptures: { 160 | "0": { 161 | name: "punctuation.definition.expression.nix", 162 | }, 163 | }, 164 | patterns: [ 165 | { 166 | include: "#expression", 167 | }, 168 | ], 169 | }, 170 | "parens-and-cont": { 171 | begin: "(?=\\()", 172 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 173 | patterns: [ 174 | { 175 | include: "#parens", 176 | }, 177 | { 178 | include: "#expression-cont", 179 | }, 180 | ], 181 | }, 182 | list: { 183 | begin: "\\[", 184 | beginCaptures: { 185 | "0": { 186 | name: "punctuation.definition.list.nix", 187 | }, 188 | }, 189 | end: "\\]", 190 | endCaptures: { 191 | "0": { 192 | name: "punctuation.definition.list.nix", 193 | }, 194 | }, 195 | patterns: [ 196 | { 197 | include: "#expression", 198 | }, 199 | ], 200 | }, 201 | "list-and-cont": { 202 | begin: "(?=\\[)", 203 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 204 | patterns: [ 205 | { 206 | include: "#list", 207 | }, 208 | { 209 | include: "#expression-cont", 210 | }, 211 | ], 212 | }, 213 | "attrset-for-sure": { 214 | patterns: [ 215 | { 216 | begin: "(?=\\brec\\b)", 217 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 218 | patterns: [ 219 | { 220 | begin: "\\brec\\b", 221 | end: "(?=\\{)", 222 | beginCaptures: { 223 | "0": { 224 | name: "keyword.other.nix", 225 | }, 226 | }, 227 | patterns: [ 228 | { 229 | include: "#others", 230 | }, 231 | ], 232 | }, 233 | { 234 | include: "#attrset-definition", 235 | }, 236 | { 237 | include: "#others", 238 | }, 239 | ], 240 | }, 241 | { 242 | begin: "(?=\\{\\s*(\\}|[^,?]*(=|;)))", 243 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 244 | patterns: [ 245 | { 246 | include: "#attrset-definition", 247 | }, 248 | { 249 | include: "#others", 250 | }, 251 | ], 252 | }, 253 | ], 254 | }, 255 | "attrset-definition": { 256 | begin: "(?=\\{)", 257 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 258 | patterns: [ 259 | { 260 | begin: "(\\{)", 261 | end: "(\\})", 262 | beginCaptures: { 263 | "0": { 264 | name: "punctuation.definition.attrset.nix", 265 | }, 266 | }, 267 | endCaptures: { 268 | "0": { 269 | name: "punctuation.definition.attrset.nix", 270 | }, 271 | }, 272 | patterns: [ 273 | { 274 | include: "#attrset-contents", 275 | }, 276 | ], 277 | }, 278 | { 279 | begin: "(?<=\\})", 280 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 281 | patterns: [ 282 | { 283 | include: "#expression-cont", 284 | }, 285 | ], 286 | }, 287 | ], 288 | }, 289 | "attrset-definition-brace-opened": { 290 | patterns: [ 291 | { 292 | begin: "(?<=\\})", 293 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 294 | patterns: [ 295 | { 296 | include: "#expression-cont", 297 | }, 298 | ], 299 | }, 300 | { 301 | begin: "(?=.?)", 302 | end: "\\}", 303 | endCaptures: { 304 | "0": { 305 | name: "punctuation.definition.attrset.nix", 306 | }, 307 | }, 308 | patterns: [ 309 | { 310 | include: "#attrset-contents", 311 | }, 312 | ], 313 | }, 314 | ], 315 | }, 316 | "attrset-contents": { 317 | patterns: [ 318 | { 319 | include: "#attribute-inherit", 320 | }, 321 | { 322 | include: "#bad-reserved", 323 | }, 324 | { 325 | include: "#attribute-bind", 326 | }, 327 | { 328 | include: "#others", 329 | }, 330 | ], 331 | }, 332 | "function-header-open-brace": { 333 | begin: "\\{", 334 | end: "(?=\\})", 335 | beginCaptures: { 336 | "0": { 337 | name: "punctuation.definition.entity.function.2.nix", 338 | }, 339 | }, 340 | patterns: [ 341 | { 342 | include: "#function-contents", 343 | }, 344 | ], 345 | }, 346 | "function-header-close-brace-no-arg": { 347 | begin: "\\}", 348 | end: "(?=\\:)", 349 | beginCaptures: { 350 | "0": { 351 | name: "punctuation.definition.entity.function.nix", 352 | }, 353 | }, 354 | patterns: [ 355 | { 356 | include: "#others", 357 | }, 358 | ], 359 | }, 360 | "function-header-terminal-arg": { 361 | begin: "(?=@)", 362 | end: "(?=\\:)", 363 | patterns: [ 364 | { 365 | begin: "\\@", 366 | end: "(?=\\:)", 367 | patterns: [ 368 | { 369 | begin: "(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*)", 370 | end: "(?=\\:)", 371 | name: "variable.parameter.function.3.nix", 372 | }, 373 | { 374 | include: "#others", 375 | }, 376 | ], 377 | }, 378 | { 379 | include: "#others", 380 | }, 381 | ], 382 | }, 383 | "function-header-close-brace-with-arg": { 384 | begin: "\\}", 385 | end: "(?=\\:)", 386 | beginCaptures: { 387 | "0": { 388 | name: "punctuation.definition.entity.function.nix", 389 | }, 390 | }, 391 | patterns: [ 392 | { 393 | include: "#function-header-terminal-arg", 394 | }, 395 | { 396 | include: "#others", 397 | }, 398 | ], 399 | }, 400 | "function-header-until-colon-no-arg": { 401 | begin: "(?=\\{)", 402 | end: "(?=\\:)", 403 | patterns: [ 404 | { 405 | include: "#function-header-open-brace", 406 | }, 407 | { 408 | include: "#function-header-close-brace-no-arg", 409 | }, 410 | ], 411 | }, 412 | "function-header-until-colon-with-arg": { 413 | begin: "(?=\\{)", 414 | end: "(?=\\:)", 415 | patterns: [ 416 | { 417 | include: "#function-header-open-brace", 418 | }, 419 | { 420 | include: "#function-header-close-brace-with-arg", 421 | }, 422 | ], 423 | }, 424 | "function-body-from-colon": { 425 | begin: "(\\:)", 426 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 427 | beginCaptures: { 428 | "0": { 429 | name: "punctuation.definition.function.nix", 430 | }, 431 | }, 432 | patterns: [ 433 | { 434 | include: "#expression", 435 | }, 436 | ], 437 | }, 438 | "function-definition": { 439 | begin: "(?=.?)", 440 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 441 | patterns: [ 442 | { 443 | include: "#function-body-from-colon", 444 | }, 445 | { 446 | begin: "(?=.?)", 447 | end: "(?=\\:)", 448 | patterns: [ 449 | { 450 | begin: "(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*)", 451 | end: "(?=\\:)", 452 | beginCaptures: { 453 | "0": { 454 | name: "variable.parameter.function.4.nix", 455 | }, 456 | }, 457 | patterns: [ 458 | { 459 | begin: "\\@", 460 | end: "(?=\\:)", 461 | patterns: [ 462 | { 463 | include: "#function-header-until-colon-no-arg", 464 | }, 465 | { 466 | include: "#others", 467 | }, 468 | ], 469 | }, 470 | { 471 | include: "#others", 472 | }, 473 | ], 474 | }, 475 | { 476 | begin: "(?=\\{)", 477 | end: "(?=\\:)", 478 | patterns: [ 479 | { 480 | include: "#function-header-until-colon-with-arg", 481 | }, 482 | ], 483 | }, 484 | ], 485 | }, 486 | { 487 | include: "#others", 488 | }, 489 | ], 490 | }, 491 | "function-definition-brace-opened": { 492 | begin: "(?=.?)", 493 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 494 | patterns: [ 495 | { 496 | include: "#function-body-from-colon", 497 | }, 498 | { 499 | begin: "(?=.?)", 500 | end: "(?=\\:)", 501 | patterns: [ 502 | { 503 | include: "#function-header-close-brace-with-arg", 504 | }, 505 | { 506 | begin: "(?=.?)", 507 | end: "(?=\\})", 508 | patterns: [ 509 | { 510 | include: "#function-contents", 511 | }, 512 | ], 513 | }, 514 | ], 515 | }, 516 | { 517 | include: "#others", 518 | }, 519 | ], 520 | }, 521 | "function-for-sure": { 522 | patterns: [ 523 | { 524 | begin: 525 | "(?=(\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*\\s*[:@]|\\{[^}\\\"']*\\}\\s*:|\\{[^#}\"'/=]*[,\\?]))", 526 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 527 | patterns: [ 528 | { 529 | include: "#function-definition", 530 | }, 531 | ], 532 | }, 533 | ], 534 | }, 535 | "function-contents": { 536 | patterns: [ 537 | { 538 | include: "#bad-reserved", 539 | }, 540 | { 541 | include: "#function-parameter", 542 | }, 543 | { 544 | include: "#others", 545 | }, 546 | ], 547 | }, 548 | "attrset-or-function": { 549 | begin: "\\{", 550 | beginCaptures: { 551 | "0": { 552 | name: "punctuation.definition.attrset-or-function.nix", 553 | }, 554 | }, 555 | end: "(?=([\\])};]|\\b(else|then)\\b))", 556 | patterns: [ 557 | { 558 | begin: 559 | "(?=(\\s*\\}|\\\"|\\binherit\\b|\\$\\{|\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*(\\s*\\.|\\s*=[^=])))", 560 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 561 | patterns: [ 562 | { 563 | include: "#attrset-definition-brace-opened", 564 | }, 565 | ], 566 | }, 567 | { 568 | begin: "(?=(\\.\\.\\.|\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*\\s*[,?]))", 569 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 570 | patterns: [ 571 | { 572 | include: "#function-definition-brace-opened", 573 | }, 574 | ], 575 | }, 576 | { 577 | include: "#bad-reserved", 578 | }, 579 | { 580 | begin: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 581 | end: "(?=([\\])};]|\\b(else|then)\\b))", 582 | beginCaptures: { 583 | "0": { 584 | name: "variable.parameter.function.maybe.nix", 585 | }, 586 | }, 587 | patterns: [ 588 | { 589 | begin: "(?=\\.)", 590 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 591 | patterns: [ 592 | { 593 | include: "#attrset-definition-brace-opened", 594 | }, 595 | ], 596 | }, 597 | { 598 | begin: "\\s*(\\,)", 599 | beginCaptures: { 600 | "1": { 601 | name: "keyword.operator.nix", 602 | }, 603 | }, 604 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 605 | patterns: [ 606 | { 607 | include: "#function-definition-brace-opened", 608 | }, 609 | ], 610 | }, 611 | { 612 | begin: "(?=\\=)", 613 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 614 | patterns: [ 615 | { 616 | include: "#attribute-bind-from-equals", 617 | }, 618 | { 619 | include: "#attrset-definition-brace-opened", 620 | }, 621 | ], 622 | }, 623 | { 624 | begin: "(?=\\?)", 625 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 626 | patterns: [ 627 | { 628 | include: "#function-parameter-default", 629 | }, 630 | { 631 | begin: "\\,", 632 | beginCaptures: { 633 | "0": { 634 | name: "keyword.operator.nix", 635 | }, 636 | }, 637 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 638 | patterns: [ 639 | { 640 | include: "#function-definition-brace-opened", 641 | }, 642 | ], 643 | }, 644 | ], 645 | }, 646 | { 647 | include: "#others", 648 | }, 649 | ], 650 | }, 651 | { 652 | include: "#others", 653 | }, 654 | ], 655 | }, 656 | "with-assert": { 657 | begin: "(?)", 928 | beginCaptures: { 929 | "0": { 930 | name: "string.unquoted.spath.nix", 931 | }, 932 | }, 933 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 934 | patterns: [ 935 | { 936 | include: "#expression-cont", 937 | }, 938 | ], 939 | }, 940 | { 941 | begin: 942 | "([a-zA-Z][a-zA-Z0-9\\+\\-\\.]*\\:[a-zA-Z0-9\\%\\/\\?\\:\\@\\&\\=\\+\\$\\,\\-\\_\\.\\!\\~\\*\\']+)", 943 | beginCaptures: { 944 | "0": { 945 | name: "string.unquoted.url.nix", 946 | }, 947 | }, 948 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 949 | patterns: [ 950 | { 951 | include: "#expression-cont", 952 | }, 953 | ], 954 | }, 955 | ], 956 | }, 957 | "parameter-name": { 958 | match: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 959 | captures: { 960 | "0": { 961 | name: "variable.parameter.name.nix", 962 | }, 963 | }, 964 | }, 965 | "parameter-name-and-cont": { 966 | begin: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 967 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 968 | beginCaptures: { 969 | "0": { 970 | name: "variable.parameter.name.nix", 971 | }, 972 | }, 973 | patterns: [ 974 | { 975 | include: "#expression-cont", 976 | }, 977 | ], 978 | }, 979 | "attribute-name-single": { 980 | match: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 981 | name: "entity.other.attribute-name.single.nix", 982 | }, 983 | "attribute-name": { 984 | patterns: [ 985 | { 986 | match: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 987 | name: "entity.other.attribute-name.multipart.nix", 988 | }, 989 | { 990 | match: "\\.", 991 | }, 992 | { 993 | include: "#string-quoted", 994 | }, 995 | { 996 | include: "#interpolation", 997 | }, 998 | ], 999 | }, 1000 | "function-parameter-default": { 1001 | begin: "\\?", 1002 | beginCaptures: { 1003 | "0": { 1004 | name: "keyword.operator.nix", 1005 | }, 1006 | }, 1007 | end: "(?=[,}])", 1008 | patterns: [ 1009 | { 1010 | include: "#expression", 1011 | }, 1012 | ], 1013 | }, 1014 | "function-parameter": { 1015 | patterns: [ 1016 | { 1017 | begin: "(\\.\\.\\.)", 1018 | end: "(,|(?=\\}))", 1019 | name: "keyword.operator.nix", 1020 | patterns: [ 1021 | { 1022 | include: "#others", 1023 | }, 1024 | ], 1025 | }, 1026 | { 1027 | begin: "\\b[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*", 1028 | beginCaptures: { 1029 | "0": { 1030 | name: "variable.parameter.function.1.nix", 1031 | }, 1032 | }, 1033 | end: "(,|(?=\\}))", 1034 | endCaptures: { 1035 | "0": { 1036 | name: "keyword.operator.nix", 1037 | }, 1038 | }, 1039 | patterns: [ 1040 | { 1041 | include: "#whitespace", 1042 | }, 1043 | { 1044 | include: "#comment", 1045 | }, 1046 | { 1047 | include: "#function-parameter-default", 1048 | }, 1049 | { 1050 | include: "#expression", 1051 | }, 1052 | ], 1053 | }, 1054 | { 1055 | include: "#others", 1056 | }, 1057 | ], 1058 | }, 1059 | "attribute-inherit": { 1060 | begin: "\\binherit\\b", 1061 | beginCaptures: { 1062 | "0": { 1063 | name: "keyword.other.inherit.nix", 1064 | }, 1065 | }, 1066 | end: "\\;", 1067 | endCaptures: { 1068 | "0": { 1069 | name: "punctuation.terminator.inherit.nix", 1070 | }, 1071 | }, 1072 | patterns: [ 1073 | { 1074 | begin: "\\(", 1075 | end: "(?=\\;)", 1076 | beginCaptures: { 1077 | "0": { 1078 | name: "punctuation.section.function.arguments.nix", 1079 | }, 1080 | }, 1081 | patterns: [ 1082 | { 1083 | begin: "\\)", 1084 | end: "(?=\\;)", 1085 | beginCaptures: { 1086 | "0": { 1087 | name: "punctuation.section.function.arguments.nix", 1088 | }, 1089 | }, 1090 | patterns: [ 1091 | { 1092 | include: "#bad-reserved", 1093 | }, 1094 | { 1095 | include: "#attribute-name-single", 1096 | }, 1097 | { 1098 | include: "#others", 1099 | }, 1100 | ], 1101 | }, 1102 | { 1103 | include: "#expression", 1104 | }, 1105 | ], 1106 | }, 1107 | { 1108 | begin: "(?=[a-zA-Z\\_])", 1109 | end: "(?=\\;)", 1110 | patterns: [ 1111 | { 1112 | include: "#bad-reserved", 1113 | }, 1114 | { 1115 | include: "#attribute-name-single", 1116 | }, 1117 | { 1118 | include: "#others", 1119 | }, 1120 | ], 1121 | }, 1122 | { 1123 | include: "#others", 1124 | }, 1125 | ], 1126 | }, 1127 | "attribute-bind-from-equals": { 1128 | begin: "\\=", 1129 | beginCaptures: { 1130 | "0": { 1131 | name: "keyword.operator.bind.nix", 1132 | }, 1133 | }, 1134 | end: "\\;", 1135 | endCaptures: { 1136 | "0": { 1137 | name: "punctuation.terminator.bind.nix", 1138 | }, 1139 | }, 1140 | patterns: [ 1141 | { 1142 | include: "#expression", 1143 | }, 1144 | ], 1145 | }, 1146 | "attribute-bind": { 1147 | patterns: [ 1148 | { 1149 | include: "#attribute-name", 1150 | }, 1151 | { 1152 | include: "#attribute-bind-from-equals", 1153 | }, 1154 | ], 1155 | }, 1156 | "operator-unary": { 1157 | name: "keyword.operator.unary.nix", 1158 | match: "(!|-)", 1159 | }, 1160 | "operator-binary": { 1161 | name: "keyword.operator.nix", 1162 | match: 1163 | "(\\bor\\b|\\.|\\|\\>|\\<\\||==|!=|!|\\<\\=|\\<|\\>\\=|\\>|&&|\\|\\||-\\>|//|\\?|\\+\\+|-|\\*|/(?=([^*]|$))|\\+)", 1164 | }, 1165 | constants: { 1166 | patterns: [ 1167 | { 1168 | begin: "\\b(builtins|true|false|null)\\b", 1169 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 1170 | beginCaptures: { 1171 | "0": { 1172 | name: "constant.language.nix", 1173 | }, 1174 | }, 1175 | patterns: [ 1176 | { 1177 | include: "#expression-cont", 1178 | }, 1179 | ], 1180 | }, 1181 | { 1182 | beginCaptures: { 1183 | "0": { 1184 | name: "support.function.nix", 1185 | }, 1186 | }, 1187 | begin: 1188 | "\\b(scopedImport|import|isNull|abort|throw|baseNameOf|dirOf|removeAttrs|map|toString|derivationStrict|derivation)\\b", 1189 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 1190 | patterns: [ 1191 | { 1192 | include: "#expression-cont", 1193 | }, 1194 | ], 1195 | }, 1196 | { 1197 | beginCaptures: { 1198 | "0": { 1199 | name: "constant.numeric.nix", 1200 | }, 1201 | }, 1202 | begin: "\\b[0-9]+\\b", 1203 | end: "(?=([\\])};,]|\\b(else|then)\\b))", 1204 | patterns: [ 1205 | { 1206 | include: "#expression-cont", 1207 | }, 1208 | ], 1209 | }, 1210 | ], 1211 | }, 1212 | whitespace: { 1213 | match: "\\s+", 1214 | }, 1215 | illegal: { 1216 | match: ".", 1217 | name: "invalid.illegal", 1218 | }, 1219 | others: { 1220 | patterns: [ 1221 | { 1222 | include: "#whitespace", 1223 | }, 1224 | { 1225 | include: "#comment", 1226 | }, 1227 | { 1228 | include: "#illegal", 1229 | }, 1230 | ], 1231 | }, 1232 | "bad-reserved": { 1233 | match: 1234 | "(?