diff --git a/vscode/language-configuration.json b/vscode/language-configuration.json new file mode 100644 index 0000000..32f42e6 --- /dev/null +++ b/vscode/language-configuration.json @@ -0,0 +1,36 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["/*", "*/"] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"" }, + { "open": "`", "close": "`" } + ], + "surroundingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"" }, + { "open": "`", "close": "`" } + ], + "folding": { + "markers": { + "start": "^\\s*//\\s*region\\b", + "end": "^\\s*//\\s*endregion\\b" + } + }, + "wordPattern": "[a-zA-Z_][a-zA-Z0-9_]*", + "indentationRules": { + "increaseIndentPattern": "\\{[^}]*$", + "decreaseIndentPattern": "^\\s*\\}" + } +} diff --git a/vscode/package.json b/vscode/package.json new file mode 100644 index 0000000..9618f82 --- /dev/null +++ b/vscode/package.json @@ -0,0 +1,77 @@ +{ + "name": "bux-lang", + "displayName": "Bux Language Support", + "description": "Syntax highlighting, snippets, and LSP support for the Bux programming language", + "version": "0.1.0", + "publisher": "bux-lang", + "license": "MIT", + "icon": "icon.png", + "repository": { + "type": "git", + "url": "https://github.com/katehonz/bux" + }, + "engines": { + "vscode": "^1.85.0" + }, + "categories": [ + "Programming Languages", + "Snippets", + "Linters" + ], + "activationEvents": [ + "onLanguage:bux" + ], + "main": "./out/extension.js", + "contributes": { + "languages": [ + { + "id": "bux", + "aliases": ["Bux", "bux"], + "extensions": [".bux"], + "configuration": "./language-configuration.json", + "icon": { + "dark": "./icon.png", + "light": "./icon.png" + } + } + ], + "grammars": [ + { + "language": "bux", + "scopeName": "source.bux", + "path": "./syntaxes/bux.tmLanguage.json" + } + ], + "snippets": [ + { + "language": "bux", + "path": "./snippets/bux.json" + } + ], + "configuration": { + "title": "Bux", + "properties": { + "bux.lsp.enabled": { + "type": "boolean", + "default": true, + "description": "Enable LSP server for diagnostics and autocomplete" + }, + "bux.lsp.path": { + "type": "string", + "default": "bux-lsp", + "description": "Path to the bux-lsp binary" + } + } + } + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./" + }, + "devDependencies": { + "@types/vscode": "^1.85.0", + "typescript": "^5.0.0", + "vscode-languageclient": "^9.0.0" + } +} diff --git a/vscode/snippets/bux.json b/vscode/snippets/bux.json new file mode 100644 index 0000000..b396983 --- /dev/null +++ b/vscode/snippets/bux.json @@ -0,0 +1,162 @@ +{ + "Main function": { + "prefix": "main", + "body": [ + "func Main() -> int {", + " $1", + " return 0;", + "}" + ], + "description": "Main entry point function" + }, + "Function": { + "prefix": "func", + "body": [ + "func ${1:Name}(${2:params}) -> ${3:void} {", + " $4", + "}" + ], + "description": "Function declaration" + }, + "Async Function": { + "prefix": "afunc", + "body": [ + "async func ${1:Name}(${2:params}) -> ${3:void} {", + " $4", + "}" + ], + "description": "Async function declaration" + }, + "Variable (let)": { + "prefix": "let", + "body": [ + "let ${1:name}: ${2:Type} = ${3:value};" + ], + "description": "Immutable variable" + }, + "Variable (var)": { + "prefix": "var", + "body": [ + "var ${1:name}: ${2:Type} = ${3:value};" + ], + "description": "Mutable variable" + }, + "Struct": { + "prefix": "struct", + "body": [ + "struct ${1:Name} {", + " ${2:field}: ${3:Type};", + "}" + ], + "description": "Struct declaration" + }, + "Enum": { + "prefix": "enum", + "body": [ + "enum ${1:Name} {", + " ${2:Variant1},", + " ${3:Variant2},", + "}" + ], + "description": "Enum declaration" + }, + "If statement": { + "prefix": "if", + "body": [ + "if ${1:condition} {", + " ${2:body}", + "}" + ], + "description": "If statement" + }, + "If-else": { + "prefix": "ifelse", + "body": [ + "if ${1:condition} {", + " ${2:body}", + "} else {", + " ${3:elseBody}", + "}" + ], + "description": "If-else statement" + }, + "While loop": { + "prefix": "while", + "body": [ + "while ${1:condition} {", + " ${2:body}", + "}" + ], + "description": "While loop" + }, + "For loop": { + "prefix": "for", + "body": [ + "for ${1:item} in ${2:iterable} {", + " ${3:body}", + "}" + ], + "description": "For-in loop" + }, + "Match": { + "prefix": "match", + "body": [ + "match ${1:expr} {", + " ${2:pattern} => ${3:action},", + " _ => ${4:default},", + "}" + ], + "description": "Pattern match expression" + }, + "Import": { + "prefix": "import", + "body": [ + "import Std::${1:Module}::{${2:items}};" + ], + "description": "Import statement" + }, + "Module": { + "prefix": "module", + "body": [ + "module ${1:Name} {", + "${2:body}", + "}" + ], + "description": "Module declaration" + }, + "Extend": { + "prefix": "extend", + "body": [ + "extend ${1:Type} {", + " ${2:methods}", + "}" + ], + "description": "Extend block for methods" + }, + "@Checked": { + "prefix": "checked", + "body": [ + "@[Checked]", + "func ${1:Name}(${2:params}) -> ${3:void} {", + " ${4:body}", + "}" + ], + "description": "Checked function with borrow checker enabled" + }, + "PrintLine": { + "prefix": "pl", + "body": [ + "PrintLine(\"${1:message}\");" + ], + "description": "Print with newline" + }, + "Generic function": { + "prefix": "gfunc", + "body": [ + "func ${1:Name}<${2:T}>(${3:param}: ${2:T}) -> ${2:T} {", + " ${4:body}", + "}" + ], + "description": "Generic function" + } +} diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts new file mode 100644 index 0000000..c96645b --- /dev/null +++ b/vscode/src/extension.ts @@ -0,0 +1,54 @@ +/* Bux Language Server Protocol client for VS Code + * Launches bux-lsp binary and connects via stdin/stdout. + */ + +import * as path from 'path'; +import * as vscode from 'vscode'; +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind +} from 'vscode-languageclient/node'; + +let client: LanguageClient; + +export function activate(context: vscode.ExtensionContext) { + const config = vscode.workspace.getConfiguration('bux.lsp'); + const enabled = config.get('enabled', true); + if (!enabled) { + vscode.window.showInformationMessage('Bux LSP is disabled in settings'); + return; + } + + const lspPath = config.get('path', 'bux-lsp'); + + const serverOptions: ServerOptions = { + command: lspPath, + transport: TransportKind.stdio + }; + + const clientOptions: LanguageClientOptions = { + documentSelector: [{ scheme: 'file', language: 'bux' }], + synchronize: { + fileEvents: vscode.workspace.createFileSystemWatcher('**/*.bux') + } + }; + + client = new LanguageClient( + 'bux-lsp', + 'Bux Language Server', + serverOptions, + clientOptions + ); + + client.start(); + vscode.window.showInformationMessage('Bux LSP started'); +} + +export function deactivate(): Thenable | undefined { + if (!client) { + return undefined; + } + return client.stop(); +} diff --git a/vscode/syntaxes/bux.tmLanguage.json b/vscode/syntaxes/bux.tmLanguage.json new file mode 100644 index 0000000..c1ca00b --- /dev/null +++ b/vscode/syntaxes/bux.tmLanguage.json @@ -0,0 +1,157 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "Bux", + "scopeName": "source.bux", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#strings" + }, + { + "include": "#keywords" + }, + { + "include": "#types" + }, + { + "include": "#functions" + }, + { + "include": "#constants" + }, + { + "include": "#operators" + }, + { + "include": "#numbers" + }, + { + "include": "#attributes" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.block.bux", + "begin": "/\\*", + "end": "\\*/" + }, + { + "name": "comment.line.bux", + "match": "//.*$" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.bux", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.bux", + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.backtick.bux", + "begin": "`", + "end": "`" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "keyword.control.bux", + "match": "\\b(if|else|while|for|do|loop|in|break|continue|return|match|case|default|spawn|async|await|try|catch|throw|discard)\\b" + }, + { + "name": "keyword.declaration.bux", + "match": "\\b(func|var|let|const|type|struct|enum|union|interface|extend|module|import|extern|pub)\\b" + }, + { + "name": "keyword.operator.bux", + "match": "\\b(as|is|sizeof|comptime|static_assert|dyn)\\b" + }, + { + "name": "keyword.ownership.bux", + "match": "\\b(own|mut|borrow)\\b" + }, + { + "name": "storage.modifier.bux", + "match": "@\\[(Checked|Shared|Import)\\]" + } + ] + }, + "types": { + "patterns": [ + { + "name": "support.type.bux", + "match": "\\b(int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float32|float64|bool|char8|char32|String|void)\\b" + }, + { + "name": "entity.name.type.bux", + "match": "\\b[A-Z][a-zA-Z0-9_]*\\b" + } + ] + }, + "functions": { + "patterns": [ + { + "name": "entity.name.function.bux", + "match": "\\b([a-z_][a-zA-Z0-9_]*)(?=\\s*\\()" + } + ] + }, + "constants": { + "patterns": [ + { + "name": "constant.language.bux", + "match": "\\b(true|false|null)\\b" + }, + { + "name": "constant.numeric.bux", + "match": "\\b(true|false|null|self|super)\\b" + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.float.bux", + "match": "\\b[0-9]+\\.[0-9]+(?:[eE][+-]?[0-9]+)?\\b" + }, + { + "name": "constant.numeric.hex.bux", + "match": "\\b0[xX][0-9a-fA-F]+\\b" + }, + { + "name": "constant.numeric.bux", + "match": "\\b[0-9]+\\b" + } + ] + }, + "operators": { + "patterns": [ + { + "name": "keyword.operator.bux", + "match": "->|=>|==|!=|<=|>=|&&|\\|\\||[+\\-*/%<>=!&|^~@]" + } + ] + }, + "attributes": { + "patterns": [ + { + "name": "storage.modifier.bux", + "match": "@(Checked|Shared|Import)" + } + ] + } + } +} diff --git a/vscode/tsconfig.json b/vscode/tsconfig.json new file mode 100644 index 0000000..5da87a1 --- /dev/null +++ b/vscode/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "rootDir": "src", + "sourceMap": true, + "strict": true, + "esModuleInterop": true + }, + "include": ["src"], + "exclude": ["node_modules", ".vscode-test"] +}