diff --git a/CHANGELOG.md b/CHANGELOG.md index bc9740f9..575eacda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,26 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit. ### Added +- Syntax highlighting via tree-sitter (dart:ffi to vendored + libtree-sitter.so with embedded wasmtime). 48 grammar WASM files, + 48 highlight queries. Colors map to theme syntax tokens. + +### Changed + +- Core frame vs shipped extension boundary defined (D-046). Builtins + are frame infrastructure only; content extensions are bundled but + architecturally removable. + +### Removed + +- `builtin.jira` stub — Jira integration belongs as a third-party + extension, not a frame builtin. +- `wasm_run` and `wasm_run_flutter` dependencies — replaced by + vendored libtree-sitter.so via dart:ffi. Eliminates runtime network + download that violated POLICY.md. + +### Added + - pql skill installed via `pql init --with-skill=yes` (`.claude/skills/pql/SKILL.md`). Covers vault queries and the planning surface (decisions + tickets). diff --git a/Makefile b/Makefile index aeae4063..5832bad8 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ ifeq ($(APP_PRESENT),yes) @bin/clide --daemon & echo $$! > $(DAEMON_PID_FILE) @echo "Daemon PID $$(cat $(DAEMON_PID_FILE))" @echo "Launching Flutter app…" - -cd app && flutter run -d linux + -cd app && LD_LIBRARY_PATH=$(CURDIR)/app/native/linux-x64$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} flutter run -d linux @$(MAKE) --no-print-directory stop else @echo "(pubspec.yaml not scaffolded yet; skipping)" diff --git a/app/assets/grammars/bash.wasm b/app/assets/grammars/bash.wasm new file mode 100755 index 00000000..e0d27903 Binary files /dev/null and b/app/assets/grammars/bash.wasm differ diff --git a/app/assets/grammars/c-sharp.wasm b/app/assets/grammars/c-sharp.wasm new file mode 100755 index 00000000..5d11bd67 Binary files /dev/null and b/app/assets/grammars/c-sharp.wasm differ diff --git a/app/assets/grammars/c.wasm b/app/assets/grammars/c.wasm new file mode 100755 index 00000000..2083f8c5 Binary files /dev/null and b/app/assets/grammars/c.wasm differ diff --git a/app/assets/grammars/comment.wasm b/app/assets/grammars/comment.wasm new file mode 100755 index 00000000..bbd6ae5c Binary files /dev/null and b/app/assets/grammars/comment.wasm differ diff --git a/app/assets/grammars/cpp.wasm b/app/assets/grammars/cpp.wasm new file mode 100755 index 00000000..0ae6bf2d Binary files /dev/null and b/app/assets/grammars/cpp.wasm differ diff --git a/app/assets/grammars/css.wasm b/app/assets/grammars/css.wasm new file mode 100755 index 00000000..046ba497 Binary files /dev/null and b/app/assets/grammars/css.wasm differ diff --git a/app/assets/grammars/dart.wasm b/app/assets/grammars/dart.wasm new file mode 100755 index 00000000..500e41da Binary files /dev/null and b/app/assets/grammars/dart.wasm differ diff --git a/app/assets/grammars/diff.wasm b/app/assets/grammars/diff.wasm new file mode 100755 index 00000000..28a893a9 Binary files /dev/null and b/app/assets/grammars/diff.wasm differ diff --git a/app/assets/grammars/dockerfile.wasm b/app/assets/grammars/dockerfile.wasm new file mode 100755 index 00000000..e842be4d Binary files /dev/null and b/app/assets/grammars/dockerfile.wasm differ diff --git a/app/assets/grammars/elixir.wasm b/app/assets/grammars/elixir.wasm new file mode 100755 index 00000000..6f12a8d0 Binary files /dev/null and b/app/assets/grammars/elixir.wasm differ diff --git a/app/assets/grammars/erlang.wasm b/app/assets/grammars/erlang.wasm new file mode 100755 index 00000000..be0cd02d Binary files /dev/null and b/app/assets/grammars/erlang.wasm differ diff --git a/app/assets/grammars/gdscript.wasm b/app/assets/grammars/gdscript.wasm new file mode 100755 index 00000000..aba4bab4 Binary files /dev/null and b/app/assets/grammars/gdscript.wasm differ diff --git a/app/assets/grammars/git-config.wasm b/app/assets/grammars/git-config.wasm new file mode 100755 index 00000000..0f93d1ac Binary files /dev/null and b/app/assets/grammars/git-config.wasm differ diff --git a/app/assets/grammars/gitignore.wasm b/app/assets/grammars/gitignore.wasm new file mode 100755 index 00000000..78852317 Binary files /dev/null and b/app/assets/grammars/gitignore.wasm differ diff --git a/app/assets/grammars/glsl.wasm b/app/assets/grammars/glsl.wasm new file mode 100755 index 00000000..2d315041 Binary files /dev/null and b/app/assets/grammars/glsl.wasm differ diff --git a/app/assets/grammars/go.wasm b/app/assets/grammars/go.wasm new file mode 100755 index 00000000..dac5b07b Binary files /dev/null and b/app/assets/grammars/go.wasm differ diff --git a/app/assets/grammars/haskell.wasm b/app/assets/grammars/haskell.wasm new file mode 100755 index 00000000..d5c1972c Binary files /dev/null and b/app/assets/grammars/haskell.wasm differ diff --git a/app/assets/grammars/hcl.wasm b/app/assets/grammars/hcl.wasm new file mode 100755 index 00000000..b2e55866 Binary files /dev/null and b/app/assets/grammars/hcl.wasm differ diff --git a/app/assets/grammars/hlsl.wasm b/app/assets/grammars/hlsl.wasm new file mode 100755 index 00000000..8769f010 Binary files /dev/null and b/app/assets/grammars/hlsl.wasm differ diff --git a/app/assets/grammars/html.wasm b/app/assets/grammars/html.wasm new file mode 100755 index 00000000..0b5fc43c Binary files /dev/null and b/app/assets/grammars/html.wasm differ diff --git a/app/assets/grammars/java.wasm b/app/assets/grammars/java.wasm new file mode 100755 index 00000000..fd5a6604 Binary files /dev/null and b/app/assets/grammars/java.wasm differ diff --git a/app/assets/grammars/javascript.wasm b/app/assets/grammars/javascript.wasm new file mode 100755 index 00000000..10b03155 Binary files /dev/null and b/app/assets/grammars/javascript.wasm differ diff --git a/app/assets/grammars/json.wasm b/app/assets/grammars/json.wasm new file mode 100755 index 00000000..a48bb2ce Binary files /dev/null and b/app/assets/grammars/json.wasm differ diff --git a/app/assets/grammars/julia.wasm b/app/assets/grammars/julia.wasm new file mode 100755 index 00000000..c22a74ec Binary files /dev/null and b/app/assets/grammars/julia.wasm differ diff --git a/app/assets/grammars/just.wasm b/app/assets/grammars/just.wasm new file mode 100755 index 00000000..5fea3166 Binary files /dev/null and b/app/assets/grammars/just.wasm differ diff --git a/app/assets/grammars/kotlin.wasm b/app/assets/grammars/kotlin.wasm new file mode 100755 index 00000000..31273e8d Binary files /dev/null and b/app/assets/grammars/kotlin.wasm differ diff --git a/app/assets/grammars/lua.wasm b/app/assets/grammars/lua.wasm new file mode 100755 index 00000000..22898f4e Binary files /dev/null and b/app/assets/grammars/lua.wasm differ diff --git a/app/assets/grammars/make.wasm b/app/assets/grammars/make.wasm new file mode 100755 index 00000000..30a69ee6 Binary files /dev/null and b/app/assets/grammars/make.wasm differ diff --git a/app/assets/grammars/markdown.wasm b/app/assets/grammars/markdown.wasm new file mode 100755 index 00000000..e9dda685 Binary files /dev/null and b/app/assets/grammars/markdown.wasm differ diff --git a/app/assets/grammars/nickel.wasm b/app/assets/grammars/nickel.wasm new file mode 100755 index 00000000..2beaace6 Binary files /dev/null and b/app/assets/grammars/nickel.wasm differ diff --git a/app/assets/grammars/nix.wasm b/app/assets/grammars/nix.wasm new file mode 100755 index 00000000..2aec3299 Binary files /dev/null and b/app/assets/grammars/nix.wasm differ diff --git a/app/assets/grammars/php.wasm b/app/assets/grammars/php.wasm new file mode 100755 index 00000000..113589f5 Binary files /dev/null and b/app/assets/grammars/php.wasm differ diff --git a/app/assets/grammars/proto.wasm b/app/assets/grammars/proto.wasm new file mode 100755 index 00000000..89e4e166 Binary files /dev/null and b/app/assets/grammars/proto.wasm differ diff --git a/app/assets/grammars/python.wasm b/app/assets/grammars/python.wasm new file mode 100755 index 00000000..3590c39d Binary files /dev/null and b/app/assets/grammars/python.wasm differ diff --git a/app/assets/grammars/r.wasm b/app/assets/grammars/r.wasm new file mode 100755 index 00000000..b94e837c Binary files /dev/null and b/app/assets/grammars/r.wasm differ diff --git a/app/assets/grammars/regex.wasm b/app/assets/grammars/regex.wasm new file mode 100755 index 00000000..f80d72cd Binary files /dev/null and b/app/assets/grammars/regex.wasm differ diff --git a/app/assets/grammars/ruby.wasm b/app/assets/grammars/ruby.wasm new file mode 100755 index 00000000..6816a56b Binary files /dev/null and b/app/assets/grammars/ruby.wasm differ diff --git a/app/assets/grammars/rust.wasm b/app/assets/grammars/rust.wasm new file mode 100755 index 00000000..34c133cb Binary files /dev/null and b/app/assets/grammars/rust.wasm differ diff --git a/app/assets/grammars/sqlite.wasm b/app/assets/grammars/sqlite.wasm new file mode 100755 index 00000000..c64da84e Binary files /dev/null and b/app/assets/grammars/sqlite.wasm differ diff --git a/app/assets/grammars/svelte.wasm b/app/assets/grammars/svelte.wasm new file mode 100755 index 00000000..86686902 Binary files /dev/null and b/app/assets/grammars/svelte.wasm differ diff --git a/app/assets/grammars/swift.wasm b/app/assets/grammars/swift.wasm new file mode 100755 index 00000000..fd5c9383 Binary files /dev/null and b/app/assets/grammars/swift.wasm differ diff --git a/app/assets/grammars/toml.wasm b/app/assets/grammars/toml.wasm new file mode 100755 index 00000000..b6fe9214 Binary files /dev/null and b/app/assets/grammars/toml.wasm differ diff --git a/app/assets/grammars/typescript.wasm b/app/assets/grammars/typescript.wasm new file mode 100755 index 00000000..d655d358 Binary files /dev/null and b/app/assets/grammars/typescript.wasm differ diff --git a/app/assets/grammars/vue.wasm b/app/assets/grammars/vue.wasm new file mode 100755 index 00000000..dec3fdc0 Binary files /dev/null and b/app/assets/grammars/vue.wasm differ diff --git a/app/assets/grammars/wgsl.wasm b/app/assets/grammars/wgsl.wasm new file mode 100755 index 00000000..1c04aba7 Binary files /dev/null and b/app/assets/grammars/wgsl.wasm differ diff --git a/app/assets/grammars/xml.wasm b/app/assets/grammars/xml.wasm new file mode 100755 index 00000000..ad93de83 Binary files /dev/null and b/app/assets/grammars/xml.wasm differ diff --git a/app/assets/grammars/yaml.wasm b/app/assets/grammars/yaml.wasm new file mode 100755 index 00000000..ceac49b3 Binary files /dev/null and b/app/assets/grammars/yaml.wasm differ diff --git a/app/assets/grammars/zig.wasm b/app/assets/grammars/zig.wasm new file mode 100755 index 00000000..a4657b6b Binary files /dev/null and b/app/assets/grammars/zig.wasm differ diff --git a/app/assets/licenses.yaml b/app/assets/licenses.yaml index 731a5fa7..80a6138b 100644 --- a/app/assets/licenses.yaml +++ b/app/assets/licenses.yaml @@ -97,6 +97,38 @@ dependencies: the core PTY wrapper for libc bindings (socketpair, recvmsg with SCM_RIGHTS, ioctl). Dart-team maintained; zero transitive deps. + - name: tree-sitter + kind: native-shared-lib + version: "0.26.8" + homepage: https://github.com/tree-sitter/tree-sitter + license: MIT + purpose: >- + Incremental parsing library with embedded WASM grammar engine. + Vendored as libtree-sitter.so (wasmtime statically linked) in + app/native/. Called via dart:ffi. Loads grammar .wasm files + through its built-in WASM store API. + + - name: wasmtime + kind: native-static-lib + version: "44.0.0" + homepage: https://github.com/bytecodealliance/wasmtime + license: Apache-2.0 with LLVM exception + purpose: >- + WebAssembly runtime. Statically linked into libtree-sitter.so to + provide grammar WASM loading. Not called directly — tree-sitter's + C API wraps it. + + - name: tree-sitter grammars + kind: wasm-assets + version: various + homepage: https://github.com/tree-sitter + license: MIT (most); Apache-2.0 (elixir, erlang, hcl, just) + purpose: >- + Language parsers for syntax highlighting. 47 grammars compiled to + WASM via wasi-sdk, loaded at runtime by tree-sitter's embedded + wasmtime engine. Source repos are tree-sitter/tree-sitter- + on GitHub; sqlite is dhcmrlchtdj/tree-sitter-sqlite (MIT). + # Build-time-only dependencies — test runners, mocks, lints. Tracked # here for audit completeness; NOT rendered in the About screen. dev_dependencies: diff --git a/app/assets/queries/bash.scm b/app/assets/queries/bash.scm new file mode 100644 index 00000000..f33a7c2d --- /dev/null +++ b/app/assets/queries/bash.scm @@ -0,0 +1,56 @@ +[ + (string) + (raw_string) + (heredoc_body) + (heredoc_start) +] @string + +(command_name) @function + +(variable_name) @property + +[ + "case" + "do" + "done" + "elif" + "else" + "esac" + "export" + "fi" + "for" + "function" + "if" + "in" + "select" + "then" + "unset" + "until" + "while" +] @keyword + +(comment) @comment + +(function_definition name: (word) @function) + +(file_descriptor) @number + +[ + (command_substitution) + (process_substitution) + (expansion) +]@embedded + +[ + "$" + "&&" + ">" + ">>" + "<" + "|" +] @operator + +( + (command (_) @constant) + (#match? @constant "^-") +) diff --git a/app/assets/queries/c-sharp.scm b/app/assets/queries/c-sharp.scm new file mode 100644 index 00000000..589598bc --- /dev/null +++ b/app/assets/queries/c-sharp.scm @@ -0,0 +1,213 @@ +(identifier) @variable + +;; Methods + +(method_declaration name: (identifier) @function) +(local_function_statement name: (identifier) @function) + +;; Types + +(interface_declaration name: (identifier) @type) +(class_declaration name: (identifier) @type) +(enum_declaration name: (identifier) @type) +(struct_declaration (identifier) @type) +(record_declaration (identifier) @type) +(namespace_declaration name: (identifier) @module) + +(generic_name (identifier) @type) +(type_parameter (identifier) @property.definition) +(parameter type: (identifier) @type) +(type_argument_list (identifier) @type) +(as_expression right: (identifier) @type) +(is_expression right: (identifier) @type) + +(constructor_declaration name: (identifier) @constructor) +(destructor_declaration name: (identifier) @constructor) + +(_ type: (identifier) @type) + +(base_list (identifier) @type) + +(predefined_type) @type.builtin + +;; Enum +(enum_member_declaration (identifier) @property.definition) + +;; Literals + +[ + (real_literal) + (integer_literal) +] @number + +[ + (character_literal) + (string_literal) + (raw_string_literal) + (verbatim_string_literal) + (interpolated_string_expression) + (interpolation_start) + (interpolation_quote) + ] @string + +(escape_sequence) @string.escape + +[ + (boolean_literal) + (null_literal) +] @constant.builtin + +;; Comments + +(comment) @comment + +;; Tokens + +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "--" + "-" + "-=" + "&" + "&=" + "&&" + "+" + "++" + "+=" + "<" + "<=" + "<<" + "<<=" + "=" + "==" + "!" + "!=" + "=>" + ">" + ">=" + ">>" + ">>=" + ">>>" + ">>>=" + "|" + "|=" + "||" + "?" + "??" + "??=" + "^" + "^=" + "~" + "*" + "*=" + "/" + "/=" + "%" + "%=" + ":" + ".." +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" + (interpolation_brace) +] @punctuation.bracket + +;; Keywords + +[ + (modifier) + "this" + (implicit_type) +] @keyword + +[ + "add" + "alias" + "as" + "base" + "break" + "case" + "catch" + "checked" + "class" + "continue" + "default" + "delegate" + "do" + "else" + "enum" + "event" + "explicit" + "extern" + "finally" + "for" + "foreach" + "global" + "goto" + "if" + "implicit" + "interface" + "is" + "lock" + "namespace" + "notnull" + "operator" + "params" + "return" + "remove" + "sizeof" + "stackalloc" + "static" + "struct" + "switch" + "throw" + "try" + "typeof" + "unchecked" + "using" + "while" + "new" + "await" + "in" + "yield" + "get" + "set" + "when" + "out" + "ref" + "from" + "where" + "select" + "record" + "init" + "with" + "let" +] @keyword + +;; Attribute + +(attribute name: (identifier) @attribute) + +;; Parameters + +(parameter + name: (identifier) @variable.parameter) + +;; Type constraints + +(type_parameter_constraints_clause (identifier) @property.definition) + +;; Method calls + +(invocation_expression (member_access_expression name: (identifier) @function)) diff --git a/app/assets/queries/c.scm b/app/assets/queries/c.scm new file mode 100644 index 00000000..8ee11890 --- /dev/null +++ b/app/assets/queries/c.scm @@ -0,0 +1,81 @@ +(identifier) @variable + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + +"break" @keyword +"case" @keyword +"const" @keyword +"continue" @keyword +"default" @keyword +"do" @keyword +"else" @keyword +"enum" @keyword +"extern" @keyword +"for" @keyword +"if" @keyword +"inline" @keyword +"return" @keyword +"sizeof" @keyword +"static" @keyword +"struct" @keyword +"switch" @keyword +"typedef" @keyword +"union" @keyword +"volatile" @keyword +"while" @keyword + +"#define" @keyword +"#elif" @keyword +"#else" @keyword +"#endif" @keyword +"#if" @keyword +"#ifdef" @keyword +"#ifndef" @keyword +"#include" @keyword +(preproc_directive) @keyword + +"--" @operator +"-" @operator +"-=" @operator +"->" @operator +"=" @operator +"!=" @operator +"*" @operator +"&" @operator +"&&" @operator +"+" @operator +"++" @operator +"+=" @operator +"<" @operator +"==" @operator +">" @operator +"||" @operator + +"." @delimiter +";" @delimiter + +(string_literal) @string +(system_lib_string) @string + +(null) @constant +(number_literal) @number +(char_literal) @number + +(field_identifier) @property +(statement_identifier) @label +(type_identifier) @type +(primitive_type) @type +(sized_type_specifier) @type + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function)) +(function_declarator + declarator: (identifier) @function) +(preproc_function_def + name: (identifier) @function.special) + +(comment) @comment diff --git a/app/assets/queries/comment.scm b/app/assets/queries/comment.scm new file mode 100644 index 00000000..87ee42ec --- /dev/null +++ b/app/assets/queries/comment.scm @@ -0,0 +1,4 @@ +(tag) @keyword +(name) @constant +(user) @string +(uri) @text.uri diff --git a/app/assets/queries/cpp.scm b/app/assets/queries/cpp.scm new file mode 100644 index 00000000..5690b5e3 --- /dev/null +++ b/app/assets/queries/cpp.scm @@ -0,0 +1,77 @@ +; Functions + +(call_expression + function: (qualified_identifier + name: (identifier) @function)) + +(template_function + name: (identifier) @function) + +(template_method + name: (field_identifier) @function) + +(template_function + name: (identifier) @function) + +(function_declarator + declarator: (qualified_identifier + name: (identifier) @function)) + +(function_declarator + declarator: (field_identifier) @function) + +; Types + +((namespace_identifier) @type + (#match? @type "^[A-Z]")) + +(auto) @type + +; Constants + +(this) @variable.builtin +(null "nullptr" @constant) + +; Modules +(module_name + (identifier) @module) + +; Keywords + +[ + "catch" + "class" + "co_await" + "co_return" + "co_yield" + "constexpr" + "constinit" + "consteval" + "delete" + "explicit" + "final" + "friend" + "mutable" + "namespace" + "noexcept" + "new" + "override" + "private" + "protected" + "public" + "template" + "throw" + "try" + "typename" + "using" + "concept" + "requires" + "virtual" + "import" + "export" + "module" +] @keyword + +; Strings + +(raw_string_literal) @string diff --git a/app/assets/queries/css.scm b/app/assets/queries/css.scm new file mode 100644 index 00000000..40c65861 --- /dev/null +++ b/app/assets/queries/css.scm @@ -0,0 +1,76 @@ +(comment) @comment + +(tag_name) @tag +(nesting_selector) @tag +(universal_selector) @tag + +"~" @operator +">" @operator +"+" @operator +"-" @operator +"*" @operator +"/" @operator +"=" @operator +"^=" @operator +"|=" @operator +"~=" @operator +"$=" @operator +"*=" @operator + +"and" @operator +"or" @operator +"not" @operator +"only" @operator + +(attribute_selector (plain_value) @string) + +((property_name) @variable + (#match? @variable "^--")) +((plain_value) @variable + (#match? @variable "^--")) + +(class_name) @property +(id_name) @property +(namespace_name) @property +(property_name) @property +(feature_name) @property + +(pseudo_element_selector (tag_name) @attribute) +(pseudo_class_selector (class_name) @attribute) +(attribute_name) @attribute + +(function_name) @function + +"@media" @keyword +"@import" @keyword +"@charset" @keyword +"@namespace" @keyword +"@supports" @keyword +"@keyframes" @keyword +(at_keyword) @keyword +(to) @keyword +(from) @keyword +(important) @keyword + +(string_value) @string +(color_value) @string.special + +(integer_value) @number +(float_value) @number +(unit) @type + +[ + "#" + "," + "." + ":" + "::" + ";" +] @punctuation.delimiter + +[ + "{" + ")" + "(" + "}" +] @punctuation.bracket diff --git a/app/assets/queries/dart.scm b/app/assets/queries/dart.scm new file mode 100644 index 00000000..d357dc70 --- /dev/null +++ b/app/assets/queries/dart.scm @@ -0,0 +1,246 @@ +; Variable +(identifier) @variable + +; Keywords +; -------------------- +[ + (assert_builtin) + (break_builtin) + (const_builtin) + (part_of_builtin) + (rethrow_builtin) + (void_type) + "abstract" + "as" + "async" + "async*" + "await" + "base" + "case" + "catch" + "class" + "continue" + "covariant" + "default" + "deferred" + "do" + "else" + "enum" + "export" + "extends" + "extension" + "external" + "factory" + "final" + "finally" + "for" + "Function" + "hide" + "if" + "implements" + "import" + "in" + "interface" + "is" + "late" + "library" + "mixin" + "new" + "on" + "operator" + "part" + "required" + "return" + "sealed" + "show" + "static" + "super" + "switch" + "sync*" + "throw" + "try" + "typedef" + "var" + "when" + "while" + "with" + "yield" +] @keyword + +; Methods +; -------------------- + +; NOTE: This query is a bit of a work around for the fact that the dart grammar doesn't +; specifically identify a node as a function call +(((identifier) @function (#match? @function "^_?[a-z]")) + . (selector . (argument_part))) @function + +; Operators and Tokens +; -------------------- +(template_substitution + "$" @punctuation.special + "{" @punctuation.special + "}" @punctuation.special +) @none + +(template_substitution + "$" @punctuation.special + (identifier_dollar_escaped) @variable +) @none + +(escape_sequence) @string.escape + +[ + "@" + "=>" + ".." + "??" + "==" + "?" + ":" + "&&" + "%" + "<" + ">" + "=" + ">=" + "<=" + "||" + "~/" + (increment_operator) + (is_operator) + (prefix_operator) + (equality_operator) + (additive_operator) +] @operator + +(type_arguments + "<" @punctuation.bracket + ">" @punctuation.bracket) + +(type_parameters + "<" @punctuation.bracket + ">" @punctuation.bracket) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Delimiters +; -------------------- +[ + ";" + "." + "," +] @punctuation.delimiter + +; Types +; -------------------- +(type_identifier) @type +((type_identifier) @type.builtin + (#match? @type.builtin "^(int|double|String|bool|List|Set|Map|Runes|Symbol)$")) +(class_definition + name: (identifier) @type) +(constructor_signature + name: (identifier) @type) +(scoped_identifier + scope: (identifier) @type) +(function_signature + name: (identifier) @function) +(getter_signature + "get" @keyword + (identifier) @function) +(setter_signature + "set" @keyword + name: (identifier) @function) + +((scoped_identifier + scope: (identifier) @type + name: (identifier) @type) + (#match? @type "^[a-zA-Z]")) + +; Enums +; ------------------- +(enum_declaration + name: (identifier) @type) +(enum_constant + name: (identifier) @identifier.constant) + +; Variables +; -------------------- +; var keyword +(inferred_type) @keyword + +((identifier) @type + (#match? @type "^_?[A-Z].*[a-z]")) + +("Function" @type) + +(this) @variable.builtin + +; properties + +(unconditional_assignable_selector + (identifier) @property) + +(conditional_assignable_selector + (identifier) @property) + +(cascade_section + (cascade_selector + (identifier) @property)) + +((selector + (unconditional_assignable_selector (identifier) @function)) + (selector (argument_part (arguments))) +) + +(cascade_section + (cascade_selector (identifier) @function) + (argument_part (arguments)) +) + +; assignments +(assignment_expression + left: (assignable_expression) @variable) + +(this) @variable.builtin + +; Parameters +; -------------------- +(formal_parameter + name: (identifier) @identifier.parameter) + +(named_argument + (label (identifier) @identifier.parameter)) + +; Literals +; -------------------- +[ + (hex_integer_literal) + (decimal_integer_literal) + (decimal_floating_point_literal) + ; TODO: inaccessbile nodes + ; (octal_integer_literal) + ; (hex_floating_point_literal) +] @number + +(string_literal) @string +(symbol_literal (identifier) @constant) @constant +(true) @boolean +(false) @boolean +(null_literal) @constant.null + +(documentation_comment) @comment +(comment) @comment + +; Annotations +; -------------------- +(annotation + "@" @attribute + name: (identifier) @attribute) diff --git a/app/assets/queries/diff.scm b/app/assets/queries/diff.scm new file mode 100644 index 00000000..54c57a80 --- /dev/null +++ b/app/assets/queries/diff.scm @@ -0,0 +1,49 @@ +(comment) @comment @spell + +[ + (addition) + (new_file) +] @diff.plus + +[ + (deletion) + (old_file) +] @diff.minus + +(commit) @constant + +(location) @attribute + +(command + "diff" @function + (argument) @variable.parameter) + +(filename) @string.special.path + +(mode) @number + +([ + ".." + "+" + "++" + "+++" + "++++" + "-" + "--" + "---" + "----" +] @punctuation.special + (#set! priority 95)) + +[ + (binary_change) + (similarity) + (file_change) +] @label + +(index + "index" @keyword) + +(similarity + (score) @number + "%" @number) diff --git a/app/assets/queries/dockerfile.scm b/app/assets/queries/dockerfile.scm new file mode 100644 index 00000000..a5d65141 --- /dev/null +++ b/app/assets/queries/dockerfile.scm @@ -0,0 +1,58 @@ +[ + "FROM" + "AS" + "RUN" + "CMD" + "LABEL" + "EXPOSE" + "ENV" + "ADD" + "COPY" + "ENTRYPOINT" + "VOLUME" + "USER" + "WORKDIR" + "ARG" + "ONBUILD" + "STOPSIGNAL" + "HEALTHCHECK" + "SHELL" + "MAINTAINER" + "CROSS_BUILD" + (heredoc_marker) + (heredoc_end) +] @keyword + +[ + ":" + "@" +] @operator + +(comment) @comment + + +(image_spec + (image_tag + ":" @punctuation.special) + (image_digest + "@" @punctuation.special)) + +[ + (double_quoted_string) + (single_quoted_string) + (json_string) + (heredoc_line) +] @string + +(expansion + [ + "$" + "{" + "}" + ] @punctuation.special +) @none + +((variable) @constant + (#match? @constant "^[A-Z][A-Z_0-9]*$")) + + diff --git a/app/assets/queries/elixir.scm b/app/assets/queries/elixir.scm new file mode 100644 index 00000000..d49f0934 --- /dev/null +++ b/app/assets/queries/elixir.scm @@ -0,0 +1,223 @@ +; Punctuation + +[ + "%" +] @punctuation + +[ + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<<" + ">>" +] @punctuation.bracket + +; Literals + +[ + (boolean) + (nil) +] @constant + +[ + (integer) + (float) +] @number + +(char) @constant + +; Identifiers + +; * regular +(identifier) @variable + +; * unused +( + (identifier) @comment.unused + (#match? @comment.unused "^_") +) + +; * special +( + (identifier) @constant.builtin + (#any-of? @constant.builtin "__MODULE__" "__DIR__" "__ENV__" "__CALLER__" "__STACKTRACE__") +) + +; Comment + +(comment) @comment + +; Quoted content + +(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded + +(escape_sequence) @string.escape + +[ + (string) + (charlist) +] @string + +[ + (atom) + (quoted_atom) + (keyword) + (quoted_keyword) +] @string.special.symbol + +; Note that we explicitly target sigil quoted start/end, so they are not overridden by delimiters + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string.special + quoted_end: _ @string.special) @string.special + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string + quoted_end: _ @string + (#match? @__name__ "^[sS]$")) @string + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string.regex + quoted_end: _ @string.regex + (#match? @__name__ "^[rR]$")) @string.regex + +; Calls + +; * local function call +(call + target: (identifier) @function) + +; * remote function call +(call + target: (dot + right: (identifier) @function)) + +; * field without parentheses or block +(call + target: (dot + right: (identifier) @property) + .) + +; * remote call without parentheses or block (overrides above) +(call + target: (dot + left: [ + (alias) + (atom) + ] + right: (identifier) @function) + .) + +; * definition keyword +(call + target: (identifier) @keyword + (#any-of? @keyword "def" "defdelegate" "defexception" "defguard" "defguardp" "defimpl" "defmacro" "defmacrop" "defmodule" "defn" "defnp" "defoverridable" "defp" "defprotocol" "defstruct")) + +; * kernel or special forms keyword +(call + target: (identifier) @keyword + (#any-of? @keyword "alias" "case" "cond" "for" "if" "import" "quote" "raise" "receive" "require" "reraise" "super" "throw" "try" "unless" "unquote" "unquote_splicing" "use" "with")) + +; * just identifier in function definition +(call + target: (identifier) @keyword + (arguments + [ + (identifier) @function + (binary_operator + left: (identifier) @function + operator: "when") + ]) + (#any-of? @keyword "def" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp" "defp")) + +; * pipe into identifier (function call) +(binary_operator + operator: "|>" + right: (identifier) @function) + +; * pipe into identifier (definition) +(call + target: (identifier) @keyword + (arguments + (binary_operator + operator: "|>" + right: (identifier) @variable)) + (#any-of? @keyword "def" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp" "defp")) + +; * pipe into field without parentheses (function call) +(binary_operator + operator: "|>" + right: (call + target: (dot + right: (identifier) @function))) + +; Operators + +; * capture operand +(unary_operator + operator: "&" + operand: (integer) @operator) + +(operator_identifier) @operator + +(unary_operator + operator: _ @operator) + +(binary_operator + operator: _ @operator) + +(dot + operator: _ @operator) + +(stab_clause + operator: _ @operator) + +; * module attribute +(unary_operator + operator: "@" @attribute + operand: [ + (identifier) @attribute + (call + target: (identifier) @attribute) + (boolean) @attribute + (nil) @attribute + ]) + +; * doc string +(unary_operator + operator: "@" @comment.doc + operand: (call + target: (identifier) @comment.doc.__attribute__ + (arguments + [ + (string) @comment.doc + (charlist) @comment.doc + (sigil + quoted_start: _ @comment.doc + quoted_end: _ @comment.doc) @comment.doc + (boolean) @comment.doc + ])) + (#any-of? @comment.doc.__attribute__ "moduledoc" "typedoc" "doc")) + +; Module + +(alias) @module + +(call + target: (dot + left: (atom) @module)) + +; Reserved keywords + +["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword diff --git a/app/assets/queries/erlang.scm b/app/assets/queries/erlang.scm new file mode 100644 index 00000000..170975a3 --- /dev/null +++ b/app/assets/queries/erlang.scm @@ -0,0 +1,46 @@ +[ + "fun" "end" + "if" "case" "of" "when" + "receive" "after" + "try" "catch" "throw" + "begin" + "module" "export" "import" + "record" "spec" "type" "opaque" "callback" + "define" "include" "include_lib" + "ifdef" "ifndef" "else" "endif" "undef" + "not" "and" "or" "andalso" "orelse" + "band" "bor" "bxor" "bnot" "bsl" "bsr" + "div" "rem" +] @keyword + +(atom) @string +(string) @string +(char) @string + +(integer) @number +(float) @number + +(comment) @comment + +(function_clause (atom) @function) +(call (atom) @function) +(attribute (atom) @attribute) + +(variable) @variable +(macro) @constant + +[ + "(" ")" "[" "]" "{" "}" "<<" ">>" +] @punctuation.bracket + +[ + "," "." ";" ":" "|" "->" "=>" "::" +] @punctuation.delimiter + +[ + "=" "==" "=:=" "=/=" "/=" + "+" "-" "*" "/" + "<" ">" "=<" ">=" + "++" "--" + "!" "#" +] @operator diff --git a/app/assets/queries/gdscript.scm b/app/assets/queries/gdscript.scm new file mode 100644 index 00000000..e4679cc1 --- /dev/null +++ b/app/assets/queries/gdscript.scm @@ -0,0 +1,59 @@ +[ + "if" "elif" "else" + "for" "while" + "match" + "break" "continue" "pass" + "return" + "class" "class_name" "extends" + "func" "static" "const" "var" "onready" + "signal" "export" "enum" + "await" "yield" + "in" "is" "as" "not" "and" "or" + "true" "false" "null" + "self" + "breakpoint" + "preload" "load" +] @keyword + +(function_definition (name) @function) +(constructor_definition (name) @function) +(call (name) @function) +(attribute_call (name) @function) + +(class_definition (name) @type) +(type) @type +(class_name_statement (name) @type) + +(string) @string +(node_path) @string +(string_name) @string +(escape_sequence) @string.special + +(integer) @number +(float) @number +(true) @boolean +(false) @boolean +(null) @constant.builtin + +(comment) @comment + +(identifier) @variable +(attribute) @property + +(annotation) @attribute + +[ + "(" ")" "[" "]" "{" "}" +] @punctuation.bracket + +[ + "," "." ":" "->" ";" +] @punctuation.delimiter + +[ + "=" "+=" "-=" "*=" "/=" "%=" + "+" "-" "*" "/" "%" + "==" "!=" "<" ">" "<=" ">=" + "&&" "||" "!" + "&" "|" "^" "~" "<<" ">>" +] @operator diff --git a/app/assets/queries/git-config.scm b/app/assets/queries/git-config.scm new file mode 100644 index 00000000..b73863bd --- /dev/null +++ b/app/assets/queries/git-config.scm @@ -0,0 +1,28 @@ +(section_name) @tag + +((section_name) @function.builtin + (#eq? @function.builtin "include")) + +((section_header + (section_name) @function.builtin + (subsection_name)) + (#eq? @function.builtin "includeIf")) + +(variable (name) @property) +[(true) (false)] @constant.builtin +(integer) @number + +[(string) (subsection_name)] @string + +((string) @string.special.path + (#match? @string.special.path "^(~|./|/)")) + +[ + "[" + "]" + "\"" +] @punctuation.bracket + +"=" @punctuation.delimiter + +(comment) @comment diff --git a/app/assets/queries/gitignore.scm b/app/assets/queries/gitignore.scm new file mode 100644 index 00000000..a361c491 --- /dev/null +++ b/app/assets/queries/gitignore.scm @@ -0,0 +1,4 @@ +(pattern) @string +(negation) @operator +(comment) @comment +(directory_separator) @punctuation.delimiter diff --git a/app/assets/queries/glsl.scm b/app/assets/queries/glsl.scm new file mode 100644 index 00000000..fe590234 --- /dev/null +++ b/app/assets/queries/glsl.scm @@ -0,0 +1,115 @@ +"break" @keyword +"case" @keyword +"const" @keyword +"continue" @keyword +"default" @keyword +"do" @keyword +"else" @keyword +"enum" @keyword +"extern" @keyword +"for" @keyword +"if" @keyword +"inline" @keyword +"return" @keyword +"sizeof" @keyword +"static" @keyword +"struct" @keyword +"switch" @keyword +"typedef" @keyword +"union" @keyword +"volatile" @keyword +"while" @keyword + +"#define" @keyword +"#elif" @keyword +"#else" @keyword +"#endif" @keyword +"#if" @keyword +"#ifdef" @keyword +"#ifndef" @keyword +"#include" @keyword +(preproc_directive) @keyword + +"--" @operator +"-" @operator +"-=" @operator +"->" @operator +"=" @operator +"!=" @operator +"*" @operator +"&" @operator +"&&" @operator +"+" @operator +"++" @operator +"+=" @operator +"<" @operator +"==" @operator +">" @operator +"||" @operator + +"." @delimiter +";" @delimiter + +(string_literal) @string +(system_lib_string) @string + +(null) @constant +(number_literal) @number +(char_literal) @number + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function)) +(function_declarator + declarator: (identifier) @function) +(preproc_function_def + name: (identifier) @function.special) + +(field_identifier) @property +(statement_identifier) @label +(type_identifier) @type +(primitive_type) @type +(sized_type_specifier) @type + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + +(identifier) @variable + +(comment) @comment +; inherits: c +[ + "in" + "out" + "inout" + "uniform" + "shared" + "layout" + "attribute" + "varying" + "buffer" + "coherent" + "readonly" + "writeonly" + "precision" + "highp" + "mediump" + "lowp" + "centroid" + "sample" + "patch" + "smooth" + "flat" + "noperspective" + "invariant" + "precise" +] @type.qualifier + +"subroutine" @keyword.function + +(extension_storage_class) @keyword.storage + +((identifier) @variable.builtin + (#lua-match? @variable.builtin "^gl_")) diff --git a/app/assets/queries/go.scm b/app/assets/queries/go.scm new file mode 100644 index 00000000..6a3c0ac8 --- /dev/null +++ b/app/assets/queries/go.scm @@ -0,0 +1,123 @@ +; Function calls + +(call_expression + function: (identifier) @function) + +(call_expression + function: (identifier) @function.builtin + (#match? @function.builtin "^(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)$")) + +(call_expression + function: (selector_expression + field: (field_identifier) @function.method)) + +; Function definitions + +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (field_identifier) @function.method) + +; Identifiers + +(type_identifier) @type +(field_identifier) @property +(identifier) @variable + +; Operators + +[ + "--" + "-" + "-=" + ":=" + "!" + "!=" + "..." + "*" + "*" + "*=" + "/" + "/=" + "&" + "&&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "++" + "+=" + "<-" + "<" + "<<" + "<<=" + "<=" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "|" + "|=" + "||" + "~" +] @operator + +; Keywords + +[ + "break" + "case" + "chan" + "const" + "continue" + "default" + "defer" + "else" + "fallthrough" + "for" + "func" + "go" + "goto" + "if" + "import" + "interface" + "map" + "package" + "range" + "return" + "select" + "struct" + "switch" + "type" + "var" +] @keyword + +; Literals + +[ + (interpreted_string_literal) + (raw_string_literal) + (rune_literal) +] @string + +(escape_sequence) @escape + +[ + (int_literal) + (float_literal) + (imaginary_literal) +] @number + +[ + (true) + (false) + (nil) + (iota) +] @constant.builtin + +(comment) @comment diff --git a/app/assets/queries/haskell.scm b/app/assets/queries/haskell.scm new file mode 100644 index 00000000..a81cced8 --- /dev/null +++ b/app/assets/queries/haskell.scm @@ -0,0 +1,443 @@ +; ---------------------------------------------------------------------------- +; Parameters and variables +; NOTE: These are at the top, so that they have low priority, +; and don't override destructured parameters +(variable) @variable + +(pattern/wildcard) @variable + +(decl/function + patterns: (patterns + (_) @variable.parameter)) + +(expression/lambda + (_)+ @variable.parameter + "->") + +(decl/function + (infix + (pattern) @variable.parameter)) + +; ---------------------------------------------------------------------------- +; Literals and comments +(integer) @number + +(negation) @number + +(expression/literal + (float)) @number.float + +(char) @character + +(string) @string + +(unit) @string.special.symbol ; unit, as in () + +(comment) @comment + +((haddock) @comment.documentation) + +; ---------------------------------------------------------------------------- +; Punctuation +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + "," + ";" +] @punctuation.delimiter + +; ---------------------------------------------------------------------------- +; Keywords, operators, includes +[ + "forall" + ; "∀" ; utf-8 is not cross-platform safe +] @keyword.repeat + +(pragma) @keyword.directive + +[ + "if" + "then" + "else" + "case" + "of" +] @keyword.conditional + +[ + "import" + "qualified" + "module" +] @keyword.import + +[ + (operator) + (constructor_operator) + (all_names) + (wildcard) + "." + ".." + "=" + "|" + "::" + "=>" + "->" + "<-" + "\\" + "`" + "@" +] @operator + +; TODO broken, also huh? +; ((qualified_module +; (module) @constructor) +; . +; (module)) + +(module + (module_id) @module) + +[ + "where" + "let" + "in" + "class" + "instance" + "pattern" + "data" + "newtype" + "family" + "type" + "as" + "hiding" + "deriving" + "via" + "stock" + "anyclass" + "do" + "mdo" + "rec" + "infix" + "infixl" + "infixr" +] @keyword + +; ---------------------------------------------------------------------------- +; Functions and variables +(decl + [ + name: (variable) @function + names: (binding_list (variable) @function) + ]) + +(decl/bind + name: (variable) @variable) + +; Consider signatures (and accompanying functions) +; with only one value on the rhs as variables +(decl/signature + name: (variable) @variable + type: (type)) + +((decl/signature + name: (variable) @_name + type: (type)) + . + (decl + name: (variable) @variable) + match: (_) + (#eq? @_name @variable)) + +; but consider a type that involves 'IO' a decl/function +(decl/signature + name: (variable) @function + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + +((decl/signature + name: (variable) @_name + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + . + (decl + name: (variable) @function) + match: (_) + (#eq? @_name @function)) + +((decl/signature) @function + . + (decl/function + name: (variable) @function)) + +(decl/bind + name: (variable) @function + (match + expression: (expression/lambda))) + +; view patterns +(view_pattern + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; consider infix functions as operators +(infix_id + [ + (variable) @operator + (qualified + (variable) @operator) + ]) + +; decl/function calls with an infix operator +; e.g. func <$> a <*> b +(infix + [ + (variable) @function.call + (qualified + ((module) @module + (variable) @function.call)) + ] + . + (operator)) + +; infix operators applied to variables +((expression/variable) @variable + . + (operator)) + +((operator) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; decl/function calls with infix operators +([ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ] + . + (operator) @_op + (#any-of? @_op "$" "<$>" ">>=" "=<<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id (variable)) + ] ; infix or `func` + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]) + . + (operator) @_op + (#any-of? @_op "$" "<$>" "=<<")) + +; decl/function composition, arrows, monadic composition (lhs) +( + [ + (expression/variable) @function + (expression/qualified + (variable) @function) + ] + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id (variable)) + ] ; infix or `func` + . + [ + (variable) @function + (qualified + (variable) @function) + ]) + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function composition, arrows, monadic composition (rhs) +((operator) @_op + . + [ + (expression/variable) @function + (expression/qualified + (variable) @function) + ] + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function defined in terms of a function composition +(decl/function + name: (variable) @function + (match + expression: (infix + operator: (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")))) + +(apply + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; function compositions, in parentheses, applied +; lhs +(apply + . + (expression/parens + (infix + [ + (variable) @function.call + (qualified + (variable) @function.call) + ] + . + (operator)))) + +; rhs +(apply + . + (expression/parens + (infix + (operator) + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]))) + +; variables being passed to a function call +(apply + (_) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; main is always a function +; (this prevents `main = undefined` from being highlighted as a variable) +(decl/bind + name: (variable) @function + (#eq? @function "main")) + +; scoped function types (func :: a -> b) +(signature + pattern: (pattern/variable) @function + type: (quantified_type)) + +; signatures that have a function type +; + binds that follow them +(decl/signature + name: (variable) @function + type: (quantified_type)) + +((decl/signature + name: (variable) @_name + type: (quantified_type)) + . + (decl/bind + (variable) @function) + (#eq? @function @_name)) + +; ---------------------------------------------------------------------------- +; Types +(name) @type + +(type/star) @type + +(variable) @type + +(constructor) @constructor + +; True or False +((constructor) @boolean + (#any-of? @boolean "True" "False")) + +; otherwise (= True) +((variable) @boolean + (#eq? @boolean "otherwise")) + +; ---------------------------------------------------------------------------- +; Quasi-quotes +(quoter) @function.call + +(quasiquote + [ + (quoter) @_name + (_ + (variable) @_name) + ] + (#eq? @_name "qq") + (quasiquote_body) @string) + +(quasiquote + (_ + (variable) @_name) + (#eq? @_name "qq") + (quasiquote_body) @string) + +; namespaced quasi-quoter +(quasiquote + (_ + (module) @module + . + (variable) @function.call)) + +; Highlighting of quasiquote_body for other languages is handled by injections.scm +; ---------------------------------------------------------------------------- +; Exceptions/error handling +((variable) @keyword.exception + (#any-of? @keyword.exception + "error" "undefined" "try" "tryJust" "tryAny" "catch" "catches" "catchJust" "handle" "handleJust" + "throw" "throwIO" "throwTo" "throwError" "ioError" "mask" "mask_" "uninterruptibleMask" + "uninterruptibleMask_" "bracket" "bracket_" "bracketOnErrorSource" "finally" "fail" + "onException" "expectationFailure")) + +; ---------------------------------------------------------------------------- +; Debugging +((variable) @keyword.debug + (#any-of? @keyword.debug + "trace" "traceId" "traceShow" "traceShowId" "traceWith" "traceShowWith" "traceStack" "traceIO" + "traceM" "traceShowM" "traceEvent" "traceEventWith" "traceEventIO" "flushEventLog" "traceMarker" + "traceMarkerIO")) + +; ---------------------------------------------------------------------------- +; Fields + +(field_name + (variable) @variable.member) + +(import_name + (name) + . + (children + (variable) @variable.member)) + + +; ---------------------------------------------------------------------------- +; Spell checking +(comment) @spell diff --git a/app/assets/queries/hcl.scm b/app/assets/queries/hcl.scm new file mode 100644 index 00000000..0521af37 --- /dev/null +++ b/app/assets/queries/hcl.scm @@ -0,0 +1,39 @@ +[ + "true" "false" "null" + "if" "else" "endif" + "for" "in" "endfor" +] @keyword + +(block (identifier) @keyword) +(block (body (block (identifier) @type))) + +(string_lit) @string +(template_literal) @string +(heredoc_template) @string +(template_interpolation) @string.special + +(numeric_lit) @number + +(comment) @comment + +(function_call (identifier) @function) + +(identifier) @variable +(variable_expr) @variable + +(attribute (identifier) @property) + +[ + "(" ")" "[" "]" "{" "}" +] @punctuation.bracket + +[ + "." "," ":" "=>" +] @punctuation.delimiter + +[ + "=" "==" "!=" "<" ">" "<=" ">=" + "+" "-" "*" "/" "%" + "&&" "||" "!" + "?" ":" +] @operator diff --git a/app/assets/queries/hlsl.scm b/app/assets/queries/hlsl.scm new file mode 100644 index 00000000..f65d58ae --- /dev/null +++ b/app/assets/queries/hlsl.scm @@ -0,0 +1,47 @@ +[ + "if" "else" + "for" "while" "do" + "switch" "case" "default" "break" "continue" + "return" "discard" + "struct" "typedef" "cbuffer" "tbuffer" + "register" "packoffset" + "in" "out" "inout" + "static" "const" "extern" "volatile" + "inline" + "true" "false" +] @keyword + +(type_identifier) @type +(primitive_type) @type.builtin + +(function_declarator (identifier) @function) +(call_expression (identifier) @function) + +(string_literal) @string + +(number_literal) @number + +(comment) @comment + +(identifier) @variable +(field_expression (identifier) @property) + +(preproc_directive) @keyword +(preproc_include) @keyword + +[ + "(" ")" "[" "]" "{" "}" +] @punctuation.bracket + +[ + "," "." ";" ":" +] @punctuation.delimiter + +[ + "=" "+=" "-=" "*=" "/=" "%=" + "+" "-" "*" "/" "%" + "==" "!=" "<" ">" "<=" ">=" + "&&" "||" "!" + "&" "|" "^" "~" "<<" ">>" + "?" ":" +] @operator diff --git a/app/assets/queries/html.scm b/app/assets/queries/html.scm new file mode 100644 index 00000000..ea0ff4e3 --- /dev/null +++ b/app/assets/queries/html.scm @@ -0,0 +1,13 @@ +(tag_name) @tag +(erroneous_end_tag_name) @tag.error +(doctype) @constant +(attribute_name) @attribute +(attribute_value) @string +(comment) @comment + +[ + "<" + ">" + "" +] @punctuation.bracket diff --git a/app/assets/queries/java.scm b/app/assets/queries/java.scm new file mode 100644 index 00000000..b13b4f46 --- /dev/null +++ b/app/assets/queries/java.scm @@ -0,0 +1,149 @@ +; Variables + +(identifier) @variable + +; Methods + +(method_declaration + name: (identifier) @function.method) +(method_invocation + name: (identifier) @function.method) +(super) @function.builtin + +; Annotations + +(annotation + name: (identifier) @attribute) +(marker_annotation + name: (identifier) @attribute) + +"@" @operator + +; Types + +(type_identifier) @type + +(interface_declaration + name: (identifier) @type) +(class_declaration + name: (identifier) @type) +(enum_declaration + name: (identifier) @type) + +((field_access + object: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_identifier + scope: (identifier) @type) + (#match? @type "^[A-Z]")) +((method_invocation + object: (identifier) @type) + (#match? @type "^[A-Z]")) +((method_reference + . (identifier) @type) + (#match? @type "^[A-Z]")) + +(constructor_declaration + name: (identifier) @type) + +[ + (boolean_type) + (integral_type) + (floating_point_type) + (floating_point_type) + (void_type) +] @type.builtin + +; Constants + +((identifier) @constant + (#match? @constant "^_*[A-Z][A-Z\\d_]+$")) + +; Builtins + +(this) @variable.builtin + +; Literals + +[ + (hex_integer_literal) + (decimal_integer_literal) + (octal_integer_literal) + (decimal_floating_point_literal) + (hex_floating_point_literal) +] @number + +[ + (character_literal) + (string_literal) +] @string +(escape_sequence) @string.escape + +[ + (true) + (false) + (null_literal) +] @constant.builtin + +[ + (line_comment) + (block_comment) +] @comment + +; Keywords + +[ + "abstract" + "assert" + "break" + "case" + "catch" + "class" + "continue" + "default" + "do" + "else" + "enum" + "exports" + "extends" + "final" + "finally" + "for" + "if" + "implements" + "import" + "instanceof" + "interface" + "module" + "native" + "new" + "non-sealed" + "open" + "opens" + "package" + "permits" + "private" + "protected" + "provides" + "public" + "requires" + "record" + "return" + "sealed" + "static" + "strictfp" + "switch" + "synchronized" + "throw" + "throws" + "to" + "transient" + "transitive" + "try" + "uses" + "volatile" + "when" + "while" + "with" + "yield" +] @keyword diff --git a/app/assets/queries/javascript.scm b/app/assets/queries/javascript.scm new file mode 100644 index 00000000..9312d682 --- /dev/null +++ b/app/assets/queries/javascript.scm @@ -0,0 +1,204 @@ +; Variables +;---------- + +(identifier) @variable + +; Properties +;----------- + +(property_identifier) @property + +; Function and method definitions +;-------------------------------- + +(function_expression + name: (identifier) @function) +(function_declaration + name: (identifier) @function) +(method_definition + name: (property_identifier) @function.method) + +(pair + key: (property_identifier) @function.method + value: [(function_expression) (arrow_function)]) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: [(function_expression) (arrow_function)]) + +(variable_declarator + name: (identifier) @function + value: [(function_expression) (arrow_function)]) + +(assignment_expression + left: (identifier) @function + right: [(function_expression) (arrow_function)]) + +; Function and method calls +;-------------------------- + +(call_expression + function: (identifier) @function) + +(call_expression + function: (member_expression + property: (property_identifier) @function.method)) + +; Special identifiers +;-------------------- + +((identifier) @constructor + (#match? @constructor "^[A-Z]")) + +([ + (identifier) + (shorthand_property_identifier) + (shorthand_property_identifier_pattern) + ] @constant + (#match? @constant "^[A-Z_][A-Z\\d_]+$")) + +((identifier) @variable.builtin + (#match? @variable.builtin "^(arguments|module|console|window|document)$") + (#is-not? local)) + +((identifier) @function.builtin + (#eq? @function.builtin "require") + (#is-not? local)) + +; Literals +;--------- + +(this) @variable.builtin +(super) @variable.builtin + +[ + (true) + (false) + (null) + (undefined) +] @constant.builtin + +(comment) @comment + +[ + (string) + (template_string) +] @string + +(regex) @string.special +(number) @number + +; Tokens +;------- + +[ + ";" + (optional_chain) + "." + "," +] @punctuation.delimiter + +[ + "-" + "--" + "-=" + "+" + "++" + "+=" + "*" + "*=" + "**" + "**=" + "/" + "/=" + "%" + "%=" + "<" + "<=" + "<<" + "<<=" + "=" + "==" + "===" + "!" + "!=" + "!==" + "=>" + ">" + ">=" + ">>" + ">>=" + ">>>" + ">>>=" + "~" + "^" + "&" + "|" + "^=" + "&=" + "|=" + "&&" + "||" + "??" + "&&=" + "||=" + "??=" +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(template_substitution + "${" @punctuation.special + "}" @punctuation.special) @embedded + +[ + "as" + "async" + "await" + "break" + "case" + "catch" + "class" + "const" + "continue" + "debugger" + "default" + "delete" + "do" + "else" + "export" + "extends" + "finally" + "for" + "from" + "function" + "get" + "if" + "import" + "in" + "instanceof" + "let" + "new" + "of" + "return" + "set" + "static" + "switch" + "target" + "throw" + "try" + "typeof" + "var" + "void" + "while" + "with" + "yield" +] @keyword diff --git a/app/assets/queries/json.scm b/app/assets/queries/json.scm new file mode 100644 index 00000000..ece8392f --- /dev/null +++ b/app/assets/queries/json.scm @@ -0,0 +1,16 @@ +(pair + key: (_) @string.special.key) + +(string) @string + +(number) @number + +[ + (null) + (true) + (false) +] @constant.builtin + +(escape_sequence) @escape + +(comment) @comment diff --git a/app/assets/queries/julia.scm b/app/assets/queries/julia.scm new file mode 100644 index 00000000..c2dda8a4 --- /dev/null +++ b/app/assets/queries/julia.scm @@ -0,0 +1,375 @@ +; Identifiers +(identifier) @variable + +(field_expression + (identifier) @variable.member .) + +; Symbols +(quote_expression + ":" @string.special.symbol + [ + (identifier) + (operator) + ] @string.special.symbol) + +; Function calls +(call_expression + (identifier) @function.call) + +(call_expression + (field_expression + (identifier) @function.call .)) + +(broadcast_call_expression + (identifier) @function.call) + +(broadcast_call_expression + (field_expression + (identifier) @function.call .)) + +(binary_expression + (_) + (operator) @_pipe + (identifier) @function.call + (#any-of? @_pipe "|>" ".|>")) + +; Macros +(macro_identifier + "@" @function.macro + (identifier) @function.macro) + +(macro_definition + (signature + (call_expression + . + (identifier) @function.macro))) + +; Built-in functions +; print.("\"", filter(name -> getglobal(Core, name) isa Core.Builtin, names(Core)), "\" ") +((identifier) @function.builtin + (#any-of? @function.builtin + "applicable" "fieldtype" "getfield" "getglobal" "invoke" "isa" "isdefined" "isdefinedglobal" + "modifyfield!" "modifyglobal!" "nfields" "replacefield!" "replaceglobal!" "setfield!" + "setfieldonce!" "setglobal!" "setglobalonce!" "swapfield!" "swapglobal!" "throw" "tuple" + "typeassert" "typeof")) + +; Type definitions +(type_head + (_) @type.definition) + +; Type annotations +(parametrized_type_expression + [ + (identifier) @type + (field_expression + (identifier) @type .) + ] + (curly_expression + (_) @type)) + +(typed_expression + (identifier) @type .) + +(unary_typed_expression + (identifier) @type .) + +(where_expression + [ + (curly_expression + (_) @type) + (_) @type + ] .) + +(unary_expression + (operator) @operator + (_) @type + (#any-of? @operator "<:" ">:")) + +(binary_expression + (_) @type + (operator) @operator + (_) @type + (#any-of? @operator "<:" ">:")) + +; Built-in types +; print.("\"", filter(name -> typeof(Base.eval(Core, name)) in [DataType, UnionAll], names(Core)), "\" ") +((identifier) @type.builtin + (#any-of? @type.builtin + "AbstractArray" "AbstractChar" "AbstractFloat" "AbstractString" "Any" "ArgumentError" "Array" + "AssertionError" "AtomicMemory" "AtomicMemoryRef" "Bool" "BoundsError" "Char" + "ConcurrencyViolationError" "Cvoid" "DataType" "DenseArray" "DivideError" "DomainError" + "ErrorException" "Exception" "Expr" "FieldError" "Float16" "Float32" "Float64" "Function" + "GenericMemory" "GenericMemoryRef" "GlobalRef" "IO" "InexactError" "InitError" "Int" "Int128" + "Int16" "Int32" "Int64" "Int8" "Integer" "InterruptException" "LineNumberNode" "LoadError" + "Memory" "MemoryRef" "Method" "MethodError" "Module" "NTuple" "NamedTuple" "Nothing" "Number" + "OutOfMemoryError" "OverflowError" "Pair" "Ptr" "QuoteNode" "ReadOnlyMemoryError" "Real" "Ref" + "SegmentationFault" "Signed" "StackOverflowError" "String" "Symbol" "Task" "Tuple" "Type" + "TypeError" "TypeVar" "UInt" "UInt128" "UInt16" "UInt32" "UInt64" "UInt8" "UndefInitializer" + "UndefKeywordError" "UndefRefError" "UndefVarError" "Union" "UnionAll" "Unsigned" "VecElement" + "WeakRef")) + +; Keywords +[ + "global" + "local" +] @keyword + +(compound_statement + [ + "begin" + "end" + ] @keyword) + +(quote_statement + [ + "quote" + "end" + ] @keyword) + +(let_statement + [ + "let" + "end" + ] @keyword) + +(if_statement + [ + "if" + "end" + ] @keyword.conditional) + +(elseif_clause + "elseif" @keyword.conditional) + +(else_clause + "else" @keyword.conditional) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(try_statement + [ + "try" + "end" + ] @keyword.exception) + +(catch_clause + "catch" @keyword.exception) + +(finally_clause + "finally" @keyword.exception) + +(for_statement + [ + "for" + "end" + ] @keyword.repeat) + +(for_binding + "outer" @keyword.repeat) + +; comprehensions +(for_clause + "for" @keyword.repeat) + +(if_clause + "if" @keyword.conditional) + +(while_statement + [ + "while" + "end" + ] @keyword.repeat) + +[ + (break_statement) + (continue_statement) +] @keyword.repeat + +[ + "const" + "mutable" +] @keyword.modifier + +(function_definition + [ + "function" + "end" + ] @keyword.function) + +(do_clause + [ + "do" + "end" + ] @keyword.function) + +(macro_definition + [ + "macro" + "end" + ] @keyword) + +(return_statement + "return" @keyword.return) + +(module_definition + [ + "module" + "baremodule" + "end" + ] @keyword.import) + +(export_statement + "export" @keyword.import) + +(public_statement + "public" @keyword.import) + +(import_statement + "import" @keyword.import) + +(using_statement + "using" @keyword.import) + +(import_alias + "as" @keyword.import) + +(selected_import + ":" @punctuation.delimiter) + +(struct_definition + [ + "mutable" + "struct" + "end" + ] @keyword.type) + +(abstract_definition + [ + "abstract" + "type" + "end" + ] @keyword.type) + +(primitive_definition + [ + "primitive" + "type" + "end" + ] @keyword.type) + +; Operators & Punctuation +(operator) @operator + +(adjoint_expression + "'" @operator) + +(range_expression + ":" @operator) + +(arrow_function_expression + "->" @operator) + +[ + "." + "..." +] @punctuation.special + +[ + "," + ";" + "::" +] @punctuation.delimiter + +; Treat `::` as operator in type contexts, see +; https://github.com/nvim-treesitter/nvim-treesitter/pull/7392 +(typed_expression + "::" @operator) + +(unary_typed_expression + "::" @operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Interpolation +(string_interpolation + . + "$" @punctuation.special) + +(interpolation_expression + . + "$" @punctuation.special) + +; Keyword operators +((operator) @keyword.operator + (#any-of? @keyword.operator "in" "isa")) + +(where_expression + "where" @keyword.operator) + +; Built-in constants +((identifier) @constant.builtin + (#any-of? @constant.builtin "nothing" "missing")) + +((identifier) @variable.builtin + (#any-of? @variable.builtin "begin" "end") + (#has-ancestor? @variable.builtin index_expression)) + +; Literals +(boolean_literal) @boolean + +(integer_literal) @number + +(float_literal) @number.float + +((identifier) @number.float + (#any-of? @number.float "NaN" "NaN16" "NaN32" "Inf" "Inf16" "Inf32")) + +(character_literal) @character + +(escape_sequence) @string.escape + +(string_literal) @string + +(prefixed_string_literal + prefix: (identifier) @function.macro) @string + +(command_literal) @string.special + +(prefixed_command_literal + prefix: (identifier) @function.macro) @string.special + +((string_literal) @string.documentation + . + [ + (abstract_definition) + (assignment) + (const_statement) + (function_definition) + (macro_definition) + (module_definition) + (struct_definition) + ]) + +(source_file + (string_literal) @string.documentation + . + [ + (identifier) + (call_expression) + ]) + +[ + (line_comment) + (block_comment) +] @comment diff --git a/app/assets/queries/just.scm b/app/assets/queries/just.scm new file mode 100644 index 00000000..e6286a86 --- /dev/null +++ b/app/assets/queries/just.scm @@ -0,0 +1,158 @@ +; File autogenerated by build-flavored-queries.py; do not edit + +; This file specifies how matched syntax patterns should be highlighted + +[ + "export" + "import" +] @keyword.import + +"mod" @module + +[ + "alias" + "set" + "shell" +] @keyword + +[ + "if" + "else" +] @keyword.conditional + +; Variables + +(value + (identifier) @variable) + +(alias + left: (identifier) @variable) + +(assignment + left: (identifier) @variable) + +; Functions + +(recipe_header + name: (identifier) @function) + +(dependency + name: (identifier) @function.call) + +(dependency_expression + name: (identifier) @function.call) + +(function_call + name: (identifier) @function.call) + +; Parameters + +(parameter + name: (identifier) @variable.parameter) + +; Namespaces + +(module + name: (identifier) @module) + +; Operators + +[ + ":=" + "?" + "==" + "!=" + "=~" + "@" + "=" + "$" + "*" + "+" + "&&" + "@-" + "-@" + "-" + "/" + ":" +] @operator + +; Punctuation + +"," @punctuation.delimiter + +[ + "{" + "}" + "[" + "]" + "(" + ")" + "{{" + "}}" +] @punctuation.bracket + +[ "`" "```" ] @punctuation.special + +; Literals + +(boolean) @boolean + +[ + (string) + (external_command) +] @string + +(escape_sequence) @string.escape + +; Comments + +(comment) @spell @comment + +(shebang) @keyword.directive + +; highlight known settings (filtering does not always work) +(setting + left: (identifier) @keyword + (#any-of? @keyword + "allow-duplicate-recipes" + "allow-duplicate-variables" + "dotenv-filename" + "dotenv-load" + "dotenv-path" + "dotenv-required" + "export" + "fallback" + "ignore-comments" + "positional-arguments" + "shell" + "shell-interpreter" + "tempdir" + "windows-powershell" + "windows-shell" + "working-directory")) + +; highlight known attributes (filtering does not always work) +(attribute + (identifier) @attribute + (#any-of? @attribute + "confirm" + "doc" + "extension" + "group" + "linux" + "macos" + "metadata" + "no-cd" + "no-exit-message" + "no-quiet" + "openbsd" + "parallel" + "positional-arguments" + "private" + "script" + "unix" + "windows" + "working-directory")) + +; Numbers are part of the syntax tree, even if disallowed +(numeric_error) @error diff --git a/app/assets/queries/kotlin.scm b/app/assets/queries/kotlin.scm new file mode 100644 index 00000000..0d991501 --- /dev/null +++ b/app/assets/queries/kotlin.scm @@ -0,0 +1,376 @@ +;; Based on the nvim-treesitter highlighting, which is under the Apache license. +;; See https://github.com/nvim-treesitter/nvim-treesitter/blob/f8ab59861eed4a1c168505e3433462ed800f2bae/queries/kotlin/highlights.scm +;; +;; The only difference in this file is that queries using #lua-match? +;; have been removed. + +;;; Identifiers + +(simple_identifier) @variable + +; `it` keyword inside lambdas +; FIXME: This will highlight the keyword outside of lambdas since tree-sitter +; does not allow us to check for arbitrary nestation +((simple_identifier) @variable.builtin +(#eq? @variable.builtin "it")) + +; `field` keyword inside property getter/setter +; FIXME: This will highlight the keyword outside of getters and setters +; since tree-sitter does not allow us to check for arbitrary nestation +((simple_identifier) @variable.builtin +(#eq? @variable.builtin "field")) + +; `this` this keyword inside classes +(this_expression) @variable.builtin + +; `super` keyword inside classes +(super_expression) @variable.builtin + +(class_parameter + (simple_identifier) @property) + +(class_body + (property_declaration + (variable_declaration + (simple_identifier) @property))) + +; id_1.id_2.id_3: `id_2` and `id_3` are assumed as object properties +(_ + (navigation_suffix + (simple_identifier) @property)) + +(enum_entry + (simple_identifier) @constant) + +(type_identifier) @type + +((type_identifier) @type.builtin + (#any-of? @type.builtin + "Byte" + "Short" + "Int" + "Long" + "UByte" + "UShort" + "UInt" + "ULong" + "Float" + "Double" + "Boolean" + "Char" + "String" + "Array" + "ByteArray" + "ShortArray" + "IntArray" + "LongArray" + "UByteArray" + "UShortArray" + "UIntArray" + "ULongArray" + "FloatArray" + "DoubleArray" + "BooleanArray" + "CharArray" + "Map" + "Set" + "List" + "EmptyMap" + "EmptySet" + "EmptyList" + "MutableMap" + "MutableSet" + "MutableList" +)) + +(package_header + . (identifier)) @namespace + +(import_header + "import" @include) + + +; TODO: Seperate labeled returns/breaks/continue/super/this +; Must be implemented in the parser first +(label) @label + +;;; Function definitions + +(function_declaration + . (simple_identifier) @function) + +(getter + ("get") @function.builtin) +(setter + ("set") @function.builtin) + +(primary_constructor) @constructor +(secondary_constructor + ("constructor") @constructor) + +(constructor_invocation + (user_type + (type_identifier) @constructor)) + +(anonymous_initializer + ("init") @constructor) + +(parameter + (simple_identifier) @parameter) + +(parameter_with_optional_type + (simple_identifier) @parameter) + +; lambda parameters +(lambda_literal + (lambda_parameters + (variable_declaration + (simple_identifier) @parameter))) + +;;; Function calls + +; function() +(call_expression + . (simple_identifier) @function) + +; object.function() or object.property.function() +(call_expression + (navigation_expression + (navigation_suffix + (simple_identifier) @function) . )) + +(call_expression + . (simple_identifier) @function.builtin + (#any-of? @function.builtin + "arrayOf" + "arrayOfNulls" + "byteArrayOf" + "shortArrayOf" + "intArrayOf" + "longArrayOf" + "ubyteArrayOf" + "ushortArrayOf" + "uintArrayOf" + "ulongArrayOf" + "floatArrayOf" + "doubleArrayOf" + "booleanArrayOf" + "charArrayOf" + "emptyArray" + "mapOf" + "setOf" + "listOf" + "emptyMap" + "emptySet" + "emptyList" + "mutableMapOf" + "mutableSetOf" + "mutableListOf" + "print" + "println" + "error" + "TODO" + "run" + "runCatching" + "repeat" + "lazy" + "lazyOf" + "enumValues" + "enumValueOf" + "assert" + "check" + "checkNotNull" + "require" + "requireNotNull" + "with" + "synchronized" +)) + +;;; Literals + +[ + (line_comment) + (multiline_comment) + (shebang_line) +] @comment + +(real_literal) @float +[ + (integer_literal) + (long_literal) + (hex_literal) + (bin_literal) + (unsigned_literal) +] @number + +[ + (null_literal) ; should be highlighted the same as booleans + (boolean_literal) +] @boolean + +(character_literal) @character + +(string_literal) @string + +(character_escape_seq) @string.escape + +; There are 3 ways to define a regex +; - "[abc]?".toRegex() +(call_expression + (navigation_expression + ((string_literal) @string.regex) + (navigation_suffix + ((simple_identifier) @_function + (#eq? @_function "toRegex"))))) + +; - Regex("[abc]?") +(call_expression + ((simple_identifier) @_function + (#eq? @_function "Regex")) + (call_suffix + (value_arguments + (value_argument + (string_literal) @string.regex)))) + +; - Regex.fromLiteral("[abc]?") +(call_expression + (navigation_expression + ((simple_identifier) @_class + (#eq? @_class "Regex")) + (navigation_suffix + ((simple_identifier) @_function + (#eq? @_function "fromLiteral")))) + (call_suffix + (value_arguments + (value_argument + (string_literal) @string.regex)))) + +;;; Keywords + +(type_alias "typealias" @keyword) +[ + (class_modifier) + (member_modifier) + (function_modifier) + (property_modifier) + (platform_modifier) + (variance_modifier) + (parameter_modifier) + (visibility_modifier) + (reification_modifier) + (inheritance_modifier) +]@keyword + +[ + "val" + "var" + "enum" + "class" + "object" + "interface" +; "typeof" ; NOTE: It is reserved for future use +] @keyword + +("fun") @keyword.function + +(jump_expression) @keyword.return + +[ + "if" + "else" + "when" +] @conditional + +[ + "for" + "do" + "while" +] @repeat + +[ + "try" + "catch" + "throw" + "finally" +] @exception + + +(annotation + "@" @attribute (use_site_target)? @attribute) +(annotation + (user_type + (type_identifier) @attribute)) +(annotation + (constructor_invocation + (user_type + (type_identifier) @attribute))) + +(file_annotation + "@" @attribute "file" @attribute ":" @attribute) +(file_annotation + (user_type + (type_identifier) @attribute)) +(file_annotation + (constructor_invocation + (user_type + (type_identifier) @attribute))) + +;;; Operators & Punctuation + +[ + "!" + "!=" + "!==" + "=" + "==" + "===" + ">" + ">=" + "<" + "<=" + "||" + "&&" + "+" + "++" + "+=" + "-" + "--" + "-=" + "*" + "*=" + "/" + "/=" + "%" + "%=" + "?." + "?:" + "!!" + "is" + "in" + "as" + "as?" + ".." + "..<" + "->" +] @operator + +[ + "(" ")" + "[" "]" + "{" "}" +] @punctuation.bracket + +[ + "." + "," + ";" + ":" + "::" +] @punctuation.delimiter + +; NOTE: `interpolated_identifier`s can be highlighted in any way +(string_literal + (interpolated_identifier) @none) +(string_literal + (interpolated_expression) @none + "}" @punctuation.special) diff --git a/app/assets/queries/lua.scm b/app/assets/queries/lua.scm new file mode 100644 index 00000000..3c8d1a82 --- /dev/null +++ b/app/assets/queries/lua.scm @@ -0,0 +1,204 @@ +; Keywords +"return" @keyword.return + +[ + "goto" + "in" + "local" + "global" +] @keyword + +(label_statement) @label + +(break_statement) @keyword + +(do_statement + [ + "do" + "end" + ] @keyword) + +(while_statement + [ + "while" + "do" + "end" + ] @repeat) + +(repeat_statement + [ + "repeat" + "until" + ] @repeat) + +(if_statement + [ + "if" + "elseif" + "else" + "then" + "end" + ] @conditional) + +(elseif_statement + [ + "elseif" + "then" + "end" + ] @conditional) + +(else_statement + [ + "else" + "end" + ] @conditional) + +(for_statement + [ + "for" + "do" + "end" + ] @repeat) + +(function_declaration + [ + "function" + "end" + ] @keyword.function) + +(function_definition + [ + "function" + "end" + ] @keyword.function) + +; Operators +(binary_expression + operator: _ @operator) + +(unary_expression + operator: _ @operator) + +"=" @operator + +[ + "and" + "not" + "or" +] @keyword.operator + +; Punctuations +[ + ";" + ":" + "," + "." +] @punctuation.delimiter + +; Brackets +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Variables +(identifier) @variable + +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) + +(variable_list + (attribute + "<" @punctuation.bracket + (identifier) @attribute + ">" @punctuation.bracket)) + +; Constants +((identifier) @constant + (#match? @constant "^[A-Z][A-Z_0-9]*$")) + +(vararg_expression) @constant + +(nil) @constant.builtin + +[ + (false) + (true) +] @boolean + +; Tables +(field + name: (identifier) @field) + +(dot_index_expression + field: (identifier) @field) + +(table_constructor + [ + "{" + "}" + ] @constructor) + +; Functions +(parameters + (identifier) @parameter) + +(function_declaration + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) + +(function_declaration + name: (method_index_expression + method: (identifier) @method)) + +(assignment_statement + (variable_list + . + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) + (expression_list + . + value: (function_definition))) + +(table_constructor + (field + name: (identifier) @function + value: (function_definition))) + +(function_call + name: [ + (identifier) @function.call + (dot_index_expression + field: (identifier) @function.call) + (method_index_expression + method: (identifier) @method.call) + ]) + +(function_call + (identifier) @function.builtin + (#any-of? @function.builtin + ; built-in functions in Lua 5.1 + "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs" "load" "loadfile" + "loadstring" "module" "next" "pairs" "pcall" "print" "rawequal" "rawget" "rawset" "require" + "select" "setfenv" "setmetatable" "tonumber" "tostring" "type" "unpack" "xpcall")) + +; Others +(comment) @comment + +(hash_bang_line) @preproc + +(number) @number + +(string) @string + +(escape_sequence) @string.escape diff --git a/app/assets/queries/make.scm b/app/assets/queries/make.scm new file mode 100644 index 00000000..9ea8016e --- /dev/null +++ b/app/assets/queries/make.scm @@ -0,0 +1,171 @@ +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +[ + ":" + "&:" + "::" + "|" + ";" + "\"" + "'" + "," +] @punctuation.delimiter + +[ + "$" + "$$" +] @punctuation.special + +(automatic_variable + [ "@" "%" "<" "?" "^" "+" "/" "*" "D" "F"] @punctuation.special) + +(automatic_variable + "/" @error . ["D" "F"]) + +[ + "=" + ":=" + "::=" + "?=" + "+=" + "!=" + "@" + "-" + "+" +] @operator + +[ + (text) + (string) + (raw_text) +] @string + +(variable_assignment (word) @string) + +[ + "ifeq" + "ifneq" + "ifdef" + "ifndef" + "else" + "endif" + "if" + "or" ; boolean functions are conditional in make grammar + "and" +] @conditional + +"foreach" @repeat + +[ + "define" + "endef" + "vpath" + "undefine" + "export" + "unexport" + "override" + "private" +; "load" +] @keyword + +[ + "include" + "sinclude" + "-include" +] @include + +[ + "subst" + "patsubst" + "strip" + "findstring" + "filter" + "filter-out" + "sort" + "word" + "words" + "wordlist" + "firstword" + "lastword" + "dir" + "notdir" + "suffix" + "basename" + "addsuffix" + "addprefix" + "join" + "wildcard" + "realpath" + "abspath" + "call" + "eval" + "file" + "value" + "shell" +] @keyword.function + +[ + "error" + "warning" + "info" +] @exception + +;; Variable +(variable_assignment + name: (word) @constant) + +(variable_reference + (word) @constant) + +(comment) @comment + +((word) @clean @string.regex + (#match? @clean "[%\*\?]")) + +(function_call + function: "error" + (arguments (text) @text.danger)) + +(function_call + function: "warning" + (arguments (text) @text.warning)) + +(function_call + function: "info" + (arguments (text) @text.note)) + +;; Install Command Categories +;; Others special variables +;; Variables Used by Implicit Rules +[ + "VPATH" + ".RECIPEPREFIX" +] @constant.builtin + +(variable_assignment + name: (word) @clean @constant.builtin + (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS)$")) + +(variable_reference + (word) @clean @constant.builtin + (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS\.VARIABLES|\.FEATURES|\.INCLUDE_DIRS|\.LOADED)$")) + +;; Standart targets +(targets + (word) @constant.macro + (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) + +(targets + (word) @constant.macro + (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) + +;; Builtin targets +(targets + (word) @constant.macro + (#match? @constant.macro "^\.(PHONY|SUFFIXES|DEFAULT|PRECIOUS|INTERMEDIATE|SECONDARY|SECONDEXPANSION|DELETE_ON_ERROR|IGNORE|LOW_RESOLUTION_TIME|SILENT|EXPORT_ALL_VARIABLES|NOTPARALLEL|ONESHELL|POSIX)$")) + diff --git a/app/assets/queries/markdown.scm b/app/assets/queries/markdown.scm new file mode 100644 index 00000000..21962fdb --- /dev/null +++ b/app/assets/queries/markdown.scm @@ -0,0 +1,47 @@ +;From nvim-treesitter/nvim-treesitter +(atx_heading + (inline) @text.title) + +(setext_heading + (paragraph) @text.title) + +[ + (atx_h1_marker) + (atx_h2_marker) + (atx_h3_marker) + (atx_h4_marker) + (atx_h5_marker) + (atx_h6_marker) + (setext_h1_underline) + (setext_h2_underline) +] @punctuation.special + +[ + (link_title) + (indented_code_block) + (fenced_code_block) +] @text.literal + +(fenced_code_block_delimiter) @punctuation.delimiter + +(code_fence_content) @none + +(link_destination) @text.uri + +(link_label) @text.reference + +[ + (list_marker_plus) + (list_marker_minus) + (list_marker_star) + (list_marker_dot) + (list_marker_parenthesis) + (thematic_break) +] @punctuation.special + +[ + (block_continuation) + (block_quote_marker) +] @punctuation.special + +(backslash_escape) @string.escape diff --git a/app/assets/queries/nickel.scm b/app/assets/queries/nickel.scm new file mode 100644 index 00000000..491d50a5 --- /dev/null +++ b/app/assets/queries/nickel.scm @@ -0,0 +1,106 @@ +(comment) @comment @spell +(annot_atom doc: (static_string) @spell) + +[ + "forall" + "in" + "let" + "default" + "doc" + "rec" + "optional" + "priority" + "force" + "not_exported" +] @keyword + +"fun" @keyword.function + +"import" @include + +[ "if" "then" "else" ] @keyword.conditional +"match" @keyword.conditional + +(types) @type +"Array" @type.builtin + +; BUILTIN Constants +(bool) @boolean +"null" @constant.builtin +(enum_tag) @constant + + +(num_literal) @number +[ + (infix_op) + "|>" + "=" + "&" + "==" + "/" + "!=" + "<" + ">" +] @operator + +(type_atom) @type + +(chunk_literal_single) @string +(chunk_literal_multi) @string + +(str_esc_char) @string.escape + +[ + "{" "}" + "(" ")" + "[|" "|]" + "[" "]" +] @punctuation.bracket + +[ + "," + "." + ":" + "|" + "->" + "+" + "-" + "*" +] @punctuation.delimiter + +(multstr_start) @punctuation.bracket +(multstr_end) @punctuation.bracket +(interpolation_start) @punctuation.special +(interpolation_end) @punctuation.special + + +(builtin) @function.builtin + +(fun_expr + (pattern_fun + (ident) @parameter + ) +) + +; application where the head terms is an identifier: function arg1 arg2 arg3 +(applicative t1: + (applicative . (record_operand (atom (ident))) @function) +) + +; application where the head terms is a record field path: foo.bar.function arg1 arg2 arg3 +(applicative t1: + (applicative . (record_operand (record_operation_chain)) @function) +) +(str_chunks) @string + +(_ + (interpolation_start) + (term) @string.special +) + +(field_path_elem) @property + +(infix_expr + op: (infix_b_op_6) + t2: (infix_expr (applicative . (record_operand (record_operation_chain) @function ))) +) diff --git a/app/assets/queries/nix.scm b/app/assets/queries/nix.scm new file mode 100644 index 00000000..918a5d5f --- /dev/null +++ b/app/assets/queries/nix.scm @@ -0,0 +1,99 @@ +(comment) @comment + +[ + "if" + "then" + "else" + "let" + "inherit" + "in" + "rec" + "with" + "assert" + "or" +] @keyword + +((identifier) @variable.builtin + (#match? @variable.builtin "^(__currentSystem|__currentTime|__langVersion|__nixPath|__nixVersion|__storeDir|builtins|false|null|true)$") + (#is-not? local)) + +((identifier) @function.builtin + (#match? @function.builtin "^(__add|__addErrorContext|__all|__any|__appendContext|__attrNames|__attrValues|__bitAnd|__bitOr|__bitXor|__catAttrs|__ceil|__compareVersions|__concatLists|__concatMap|__concatStringsSep|__deepSeq|__div|__elem|__elemAt|__fetchurl|__filter|__filterSource|__findFile|__flakeRefToString|__floor|__foldl'|__fromJSON|__functionArgs|__genList|__genericClosure|__getAttr|__getContext|__getEnv|__getFlake|__groupBy|__hasAttr|__hasContext|__hashFile|__hashString|__head|__intersectAttrs|__isAttrs|__isBool|__isFloat|__isFunction|__isInt|__isList|__isPath|__isString|__length|__lessThan|__listToAttrs|__mapAttrs|__match|__mul|__parseDrvName|__parseFlakeRef|__partition|__path|__pathExists|__readDir|__readFile|__readFileType|__replaceStrings|__seq|__sort|__split|__splitVersion|__storePath|__stringLength|__sub|__substring|__tail|__toFile|__toJSON|__toPath|__toXML|__trace|__traceVerbose|__tryEval|__typeOf|__unsafeDiscardOutputDependency|__unsafeDiscardStringContext|__unsafeGetAttrPos|__zipAttrsWith|abort|baseNameOf|break|derivation|derivationStrict|dirOf|fetchGit|fetchMercurial|fetchTarball|fetchTree|fromTOML|import|isNull|map|placeholder|removeAttrs|scopedImport|throw|toString)$") + (#is-not? local)) + +[ + (integer_expression) + (float_expression) +] @number + +(escape_sequence) @escape +(dollar_escape) @escape + +(function_expression + universal: (identifier) @variable.parameter +) + +(formal + name: (identifier) @variable.parameter + "?"? @punctuation.delimiter) + +(select_expression + attrpath: (attrpath (identifier)) @property) + +(apply_expression + function: [ + (variable_expression (identifier)) @function + (select_expression + attrpath: (attrpath + attr: (identifier) @function .))]) + +(unary_expression + operator: _ @operator) + +(binary_expression + operator: _ @operator) + +(variable_expression (identifier) @variable) + +(binding + attrpath: (attrpath (identifier)) @property) + +(identifier) @property + +(inherit_from attrs: (inherited_attrs attr: (identifier) @property) ) + +[ + ";" + "." + "," + "=" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(identifier) @variable + +[ + (string_expression) + (indented_string_expression) +] @string + +[ + (path_expression) + (hpath_expression) + (spath_expression) +] @string.special.path + +(uri_expression) @string.special.uri + +(interpolation + "${" @punctuation.special + (_) @embedded + "}" @punctuation.special) diff --git a/app/assets/queries/php.scm b/app/assets/queries/php.scm new file mode 100644 index 00000000..2bf40364 --- /dev/null +++ b/app/assets/queries/php.scm @@ -0,0 +1,203 @@ +[ + (php_tag) + (php_end_tag) +] @tag + +; Keywords + +[ + "and" + "as" + "break" + "case" + "catch" + "class" + "clone" + "const" + "continue" + "declare" + "default" + "do" + "echo" + "else" + "elseif" + "enddeclare" + "endfor" + "endforeach" + "endif" + "endswitch" + "endwhile" + "enum" + "exit" + "extends" + "finally" + "fn" + "for" + "foreach" + "function" + "global" + "goto" + "if" + "implements" + "include" + "include_once" + "instanceof" + "insteadof" + "interface" + "match" + "namespace" + "new" + "or" + "print" + "require" + "require_once" + "return" + "switch" + "throw" + "trait" + "try" + "use" + "while" + "xor" + "yield" + "yield from" + (abstract_modifier) + (final_modifier) + (readonly_modifier) + (static_modifier) + (visibility_modifier) +] @keyword + +(function_static_declaration "static" @keyword) + +; Namespace + +(namespace_definition + name: (namespace_name + (name) @module)) + +(namespace_name + (name) @module) + +(namespace_use_clause + [ + (name) @type + (qualified_name + (name) @type) + alias: (name) @type + ]) + +(namespace_use_clause + type: "function" + [ + (name) @function + (qualified_name + (name) @function) + alias: (name) @function + ]) + +(namespace_use_clause + type: "const" + [ + (name) @constant + (qualified_name + (name) @constant) + alias: (name) @constant + ]) + +(relative_name "namespace" @module.builtin) + +; Variables + +(relative_scope) @variable.builtin + +(variable_name) @variable + +(method_declaration name: (name) @constructor + (#eq? @constructor "__construct")) + +(object_creation_expression [ + (name) @constructor + (qualified_name (name) @constructor) + (relative_name (name) @constructor) +]) + +((name) @constant + (#match? @constant "^_?[A-Z][A-Z\\d_]+$")) +((name) @constant.builtin + (#match? @constant.builtin "^__[A-Z][A-Z\d_]+__$")) +(const_declaration (const_element (name) @constant)) + +; Types + +(primitive_type) @type.builtin +(cast_type) @type.builtin +(named_type [ + (name) @type + (qualified_name (name) @type) + (relative_name (name) @type) +]) @type +(named_type (name) @type.builtin + (#any-of? @type.builtin "static" "self")) + +(scoped_call_expression + scope: [ + (name) @type + (qualified_name (name) @type) + (relative_name (name) @type) + ]) + +; Functions + +(array_creation_expression "array" @function.builtin) +(list_literal "list" @function.builtin) +(exit_statement "exit" @function.builtin "(") + +(method_declaration + name: (name) @function.method) + +(function_call_expression + function: [ + (qualified_name (name)) + (relative_name (name)) + (name) + ] @function) + +(scoped_call_expression + name: (name) @function) + +(member_call_expression + name: (name) @function.method) + +(function_definition + name: (name) @function) + +; Member + +(property_element + (variable_name) @property) + +(member_access_expression + name: (variable_name (name)) @property) +(member_access_expression + name: (name) @property) + +; Basic tokens +[ + (string) + (string_content) + (encapsed_string) + (heredoc) + (heredoc_body) + (nowdoc_body) +] @string +(boolean) @constant.builtin +(null) @constant.builtin +(integer) @number +(float) @number +(comment) @comment + +((name) @variable.builtin + (#eq? @variable.builtin "this")) + +"$" @operator diff --git a/app/assets/queries/proto.scm b/app/assets/queries/proto.scm new file mode 100644 index 00000000..db3fee7b --- /dev/null +++ b/app/assets/queries/proto.scm @@ -0,0 +1,48 @@ +[ + "syntax" + "package" + "option" + "import" + "service" + "rpc" + "returns" + "message" + "enum" + "oneof" + "repeated" + "reserved" + "to" +] @keyword + +[ + (key_type) + (type) + (message_name) + (enum_name) + (service_name) + (rpc_name) +]@type + +(string) @string + +[ + (int_lit) + (float_lit) +] @number + +[ + (true) + (false) +] @constant.builtin + +(comment) @comment + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + diff --git a/app/assets/queries/python.scm b/app/assets/queries/python.scm new file mode 100644 index 00000000..af744484 --- /dev/null +++ b/app/assets/queries/python.scm @@ -0,0 +1,137 @@ +; Identifier naming conventions + +(identifier) @variable + +((identifier) @constructor + (#match? @constructor "^[A-Z]")) + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z_]*$")) + +; Function calls + +(decorator) @function +(decorator + (identifier) @function) + +(call + function: (attribute attribute: (identifier) @function.method)) +(call + function: (identifier) @function) + +; Builtin functions + +((call + function: (identifier) @function.builtin) + (#match? + @function.builtin + "^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$")) + +; Function definitions + +(function_definition + name: (identifier) @function) + +(attribute attribute: (identifier) @property) +(type (identifier) @type) + +; Literals + +[ + (none) + (true) + (false) +] @constant.builtin + +[ + (integer) + (float) +] @number + +(comment) @comment +(string) @string +(escape_sequence) @escape + +(interpolation + "{" @punctuation.special + "}" @punctuation.special) @embedded + +[ + "-" + "-=" + "!=" + "*" + "**" + "**=" + "*=" + "/" + "//" + "//=" + "/=" + "&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "->" + "+=" + "<" + "<<" + "<<=" + "<=" + "<>" + "=" + ":=" + "==" + ">" + ">=" + ">>" + ">>=" + "|" + "|=" + "~" + "@=" + "and" + "in" + "is" + "not" + "or" + "is not" + "not in" +] @operator + +[ + "as" + "assert" + "async" + "await" + "break" + "class" + "continue" + "def" + "del" + "elif" + "else" + "except" + "exec" + "finally" + "for" + "from" + "global" + "if" + "import" + "lambda" + "nonlocal" + "pass" + "print" + "raise" + "return" + "try" + "while" + "with" + "yield" + "match" + "case" +] @keyword diff --git a/app/assets/queries/r.scm b/app/assets/queries/r.scm new file mode 100644 index 00000000..c632d3e0 --- /dev/null +++ b/app/assets/queries/r.scm @@ -0,0 +1,112 @@ +; highlights.scm + +; Literals + +(integer) @number +(float) @number +(complex) @number + +(string) @string +(string (string_content (escape_sequence) @string.escape)) + +; Comments + +(comment) @comment + +; Operators + +[ + "?" ":=" "=" "<-" "<<-" "->" "->>" + "~" "|>" "||" "|" "&&" "&" + "<" "<=" ">" ">=" "==" "!=" + "+" "-" "*" "/" "::" ":::" + "**" "^" "$" "@" ":" "!" + "special" +] @operator + +; Punctuation + +[ + "(" ")" + "{" "}" + "[" "]" + "[[" "]]" +] @punctuation.bracket + +(comma) @punctuation.delimiter + +; Variables + +(identifier) @variable + +; Functions + +(binary_operator + lhs: (identifier) @function + operator: "<-" + rhs: (function_definition) +) + +(binary_operator + lhs: (identifier) @function + operator: "=" + rhs: (function_definition) +) + +; Calls + +(call function: (identifier) @function) + +; Parameters + +(parameters (parameter name: (identifier) @variable.parameter)) +(arguments (argument name: (identifier) @variable.parameter)) + +; Namespace + +(namespace_operator lhs: (identifier) @namespace) + +(call + function: (namespace_operator rhs: (identifier) @function) +) + +; Keywords + +(function_definition name: "function" @keyword.function) +(function_definition name: "\\" @operator) + +[ + "in" + (return) + (next) + (break) +] @keyword + +[ + "if" + "else" +] @conditional + +[ + "while" + "repeat" + "for" +] @repeat + +[ + (true) + (false) +] @boolean + +[ + (null) + (inf) + (nan) + (na) + (dots) + (dot_dot_i) +] @constant.builtin + +; Error + +(ERROR) @error diff --git a/app/assets/queries/regex.scm b/app/assets/queries/regex.scm new file mode 100644 index 00000000..d062cbbe --- /dev/null +++ b/app/assets/queries/regex.scm @@ -0,0 +1,63 @@ +[ + "(" + ")" + "(?" + "(?:" + "(?<" + "(?P<" + "(?P=" + ">" + "[" + "]" + "{" + "}" + "[:" + ":]" +] @punctuation.bracket + +(group_name) @property + +[ + (identity_escape) + (control_letter_escape) + (character_class_escape) + (control_escape) + (start_assertion) + (end_assertion) + (boundary_assertion) + (non_boundary_assertion) +] @escape + +[ + "*" + "+" + "?" + "|" + "=" + "!" +] @operator + +(count_quantifier + [ + (decimal_digits) @number + "," @punctuation.delimiter + ]) + +(inline_flags_group + "-"? @operator + ":"? @punctuation.delimiter) + +(flags) @character.special + +(character_class + [ + "^" @operator + (class_range "-" @operator) + ]) + +[ + (class_character) + (posix_class_name) +] @constant.character + +(pattern_character) @string diff --git a/app/assets/queries/ruby.scm b/app/assets/queries/ruby.scm new file mode 100644 index 00000000..dd1c9139 --- /dev/null +++ b/app/assets/queries/ruby.scm @@ -0,0 +1,154 @@ +(identifier) @variable + +((identifier) @function.method + (#is-not? local)) + +[ + "alias" + "and" + "begin" + "break" + "case" + "class" + "def" + "do" + "else" + "elsif" + "end" + "ensure" + "for" + "if" + "in" + "module" + "next" + "or" + "rescue" + "retry" + "return" + "then" + "unless" + "until" + "when" + "while" + "yield" +] @keyword + +((identifier) @keyword + (#match? @keyword "^(private|protected|public)$")) + +(constant) @constructor + +; Function calls + +"defined?" @function.method.builtin + +(call + method: [(identifier) (constant)] @function.method) + +((identifier) @function.method.builtin + (#eq? @function.method.builtin "require")) + +; Function definitions + +(alias (identifier) @function.method) +(setter (identifier) @function.method) +(method name: [(identifier) (constant)] @function.method) +(singleton_method name: [(identifier) (constant)] @function.method) + +; Identifiers + +[ + (class_variable) + (instance_variable) +] @property + +((identifier) @constant.builtin + (#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$")) + +(file) @constant.builtin +(line) @constant.builtin +(encoding) @constant.builtin + +(hash_splat_nil + "**" @operator) @constant.builtin + +((constant) @constant + (#match? @constant "^[A-Z\\d_]+$")) + +[ + (self) + (super) +] @variable.builtin + +(block_parameter (identifier) @variable.parameter) +(block_parameters (identifier) @variable.parameter) +(destructured_parameter (identifier) @variable.parameter) +(hash_splat_parameter (identifier) @variable.parameter) +(lambda_parameters (identifier) @variable.parameter) +(method_parameters (identifier) @variable.parameter) +(splat_parameter (identifier) @variable.parameter) + +(keyword_parameter name: (identifier) @variable.parameter) +(optional_parameter name: (identifier) @variable.parameter) + +; Literals + +[ + (string) + (bare_string) + (subshell) + (heredoc_body) + (heredoc_beginning) +] @string + +[ + (simple_symbol) + (delimited_symbol) + (hash_key_symbol) + (bare_symbol) +] @string.special.symbol + +(regex) @string.special.regex +(escape_sequence) @escape + +[ + (integer) + (float) +] @number + +[ + (nil) + (true) + (false) +] @constant.builtin + +(interpolation + "#{" @punctuation.special + "}" @punctuation.special) @embedded + +(comment) @comment + +; Operators + +[ +"=" +"=>" +"->" +] @operator + +[ + "," + ";" + "." +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "%w(" + "%i(" +] @punctuation.bracket diff --git a/app/assets/queries/rust.scm b/app/assets/queries/rust.scm new file mode 100644 index 00000000..48c7284e --- /dev/null +++ b/app/assets/queries/rust.scm @@ -0,0 +1,161 @@ +; Identifiers + +(type_identifier) @type +(primitive_type) @type.builtin +(field_identifier) @property + +; Identifier conventions + +; Assume all-caps names are constants +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]+$'")) + +; Assume uppercase names are enum constructors +((identifier) @constructor + (#match? @constructor "^[A-Z]")) + +; Assume that uppercase names in paths are types +((scoped_identifier + path: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_identifier + path: (scoped_identifier + name: (identifier) @type)) + (#match? @type "^[A-Z]")) +((scoped_type_identifier + path: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_type_identifier + path: (scoped_identifier + name: (identifier) @type)) + (#match? @type "^[A-Z]")) + +; Assume all qualified names in struct patterns are enum constructors. (They're +; either that, or struct names; highlighting both as constructors seems to be +; the less glaring choice of error, visually.) +(struct_pattern + type: (scoped_type_identifier + name: (type_identifier) @constructor)) + +; Function calls + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function.method)) +(call_expression + function: (scoped_identifier + "::" + name: (identifier) @function)) + +(generic_function + function: (identifier) @function) +(generic_function + function: (scoped_identifier + name: (identifier) @function)) +(generic_function + function: (field_expression + field: (field_identifier) @function.method)) + +(macro_invocation + macro: (identifier) @function.macro + "!" @function.macro) + +; Function definitions + +(function_item (identifier) @function) +(function_signature_item (identifier) @function) + +(line_comment) @comment +(block_comment) @comment + +(line_comment (doc_comment)) @comment.documentation +(block_comment (doc_comment)) @comment.documentation + +"(" @punctuation.bracket +")" @punctuation.bracket +"[" @punctuation.bracket +"]" @punctuation.bracket +"{" @punctuation.bracket +"}" @punctuation.bracket + +(type_arguments + "<" @punctuation.bracket + ">" @punctuation.bracket) +(type_parameters + "<" @punctuation.bracket + ">" @punctuation.bracket) + +"::" @punctuation.delimiter +":" @punctuation.delimiter +"." @punctuation.delimiter +"," @punctuation.delimiter +";" @punctuation.delimiter + +(parameter (identifier) @variable.parameter) + +(lifetime (identifier) @label) + +"as" @keyword +"async" @keyword +"await" @keyword +"break" @keyword +"const" @keyword +"continue" @keyword +"default" @keyword +"dyn" @keyword +"else" @keyword +"enum" @keyword +"extern" @keyword +"fn" @keyword +"for" @keyword +"gen" @keyword +"if" @keyword +"impl" @keyword +"in" @keyword +"let" @keyword +"loop" @keyword +"macro_rules!" @keyword +"match" @keyword +"mod" @keyword +"move" @keyword +"pub" @keyword +"raw" @keyword +"ref" @keyword +"return" @keyword +"static" @keyword +"struct" @keyword +"trait" @keyword +"type" @keyword +"union" @keyword +"unsafe" @keyword +"use" @keyword +"where" @keyword +"while" @keyword +"yield" @keyword +(crate) @keyword +(mutable_specifier) @keyword +(use_list (self) @keyword) +(scoped_use_list (self) @keyword) +(scoped_identifier (self) @keyword) +(super) @keyword + +(self) @variable.builtin + +(char_literal) @string +(string_literal) @string +(raw_string_literal) @string + +(boolean_literal) @constant.builtin +(integer_literal) @constant.builtin +(float_literal) @constant.builtin + +(escape_sequence) @escape + +(attribute_item) @attribute +(inner_attribute_item) @attribute + +"*" @operator +"&" @operator +"'" @operator diff --git a/app/assets/queries/sqlite.scm b/app/assets/queries/sqlite.scm new file mode 100644 index 00000000..24a8cd44 --- /dev/null +++ b/app/assets/queries/sqlite.scm @@ -0,0 +1,179 @@ +;;; + +";" @punctuation.delimiter +"," @punctuation.delimiter +"." @punctuation.delimiter + +"(" @punctuation.bracket +")" @punctuation.bracket + +[ + "||" + "~" "&" "|" "<<" ">>" + "+" "-" "*" "/" "%" + "=" "==" "!=" "<>" ">" ">=" "<" "<=" + "->" "->>" +] @operator + +;;; + +(signed_number) @number + +(numeric_literal) @number +(string_literal) @string +(blob_literal) @string.special +(identifier) @constant +(bind_parameter) @variable.parameter +(comment) @comment + +;;; + +(ABORT) @keyword +(ACTION) @keyword +(ADD) @keyword +(AFTER) @keyword +(ALL) @keyword +(ALTER) @keyword +(ALWAYS) @keyword +(ANALYZE) @keyword +(AND) @keyword +(AS) @keyword +(ASC) @keyword +(ATTACH) @keyword +(AUTOINCREMENT) @keyword +(BEFORE) @keyword +(BEGIN) @keyword +(BETWEEN) @keyword +(BY) @keyword +(CASCADE) @keyword +(CASE) @keyword +(CAST) @keyword +(CHECK) @keyword +(COLLATE) @keyword +(COLUMN) @keyword +(COMMIT) @keyword +(CONFLICT) @keyword +(CONSTRAINT) @keyword +(CREATE) @keyword +(CROSS) @keyword +(CURRENT) @keyword +(CURRENT_DATE) @keyword +(CURRENT_TIME) @keyword +(CURRENT_TIMESTAMP) @keyword +(DATABASE) @keyword +(DEFAULT) @keyword +(DEFERRABLE) @keyword +(DEFERRED) @keyword +(DELETE) @keyword +(DESC) @keyword +(DETACH) @keyword +(DISTINCT) @keyword +(DO) @keyword +(DROP) @keyword +(EACH) @keyword +(ELSE) @keyword +(END) @keyword +(ESCAPE) @keyword +(EXCEPT) @keyword +(EXCLUDE) @keyword +(EXCLUSIVE) @keyword +(EXISTS) @keyword +(EXPLAIN) @keyword +(FAIL) @keyword +(FALSE) @keyword +(FILTER) @keyword +(FIRST) @keyword +(FOLLOWING) @keyword +(FOR) @keyword +(FOREIGN) @keyword +(FROM) @keyword +(GENERATED) @keyword +(GLOB) @keyword +(GROUP) @keyword +(GROUPS) @keyword +(HAVING) @keyword +(IF) @keyword +(IGNORE) @keyword +(IMMEDIATE) @keyword +(IN) @keyword +(INDEX) @keyword +(INDEXED) @keyword +(INITIALLY) @keyword +(INNER) @keyword +(INSERT) @keyword +(INSTEAD) @keyword +(INTERSECT) @keyword +(INTO) @keyword +(IS) @keyword +(ISNULL) @keyword +(JOIN) @keyword +(KEY) @keyword +(LAST) @keyword +(LEFT) @keyword +(LIKE) @keyword +(LIMIT) @keyword +(MATCH) @keyword +(MATERIALIZED) @keyword +(NATURAL) @keyword +(NO) @keyword +(NOT) @keyword +(NOTHING) @keyword +(NOTNULL) @keyword +(NULL) @keyword +(NULLS) @keyword +(OF) @keyword +(OFFSET) @keyword +(ON) @keyword +(OR) @keyword +(ORDER) @keyword +(OTHERS) @keyword +(OUTER) @keyword +(OVER) @keyword +(PARTITION) @keyword +(PLAN) @keyword +(PRAGMA) @keyword +(PRECEDING) @keyword +(PRIMARY) @keyword +(QUERY) @keyword +(RAISE) @keyword +(RANGE) @keyword +(RECURSIVE) @keyword +(REFERENCES) @keyword +(REGEXP) @keyword +(REINDEX) @keyword +(RELEASE) @keyword +(RENAME) @keyword +(REPLACE) @keyword +(RESTRICT) @keyword +(RETURNING) @keyword +(ROLLBACK) @keyword +(ROW) @keyword +(ROWID) @keyword +(ROWS) @keyword +(SAVEPOINT) @keyword +(SELECT) @keyword +(SET) @keyword +(STORED) @keyword +(TABLE) @keyword +(TEMP) @keyword +(TEMPORARY) @keyword +(THEN) @keyword +(TIES) @keyword +(TO) @keyword +(TRANSACTION) @keyword +(TRIGGER) @keyword +(TRUE) @keyword +(UNBOUNDED) @keyword +(UNION) @keyword +(UNIQUE) @keyword +(UPDATE) @keyword +(USING) @keyword +(VACUUM) @keyword +(VALUES) @keyword +(VIEW) @keyword +(VIRTUAL) @keyword +(WHEN) @keyword +(WHERE) @keyword +(WINDOW) @keyword +(WITH) @keyword +(WITHOUT) @keyword diff --git a/app/assets/queries/svelte.scm b/app/assets/queries/svelte.scm new file mode 100644 index 00000000..961a8ed5 --- /dev/null +++ b/app/assets/queries/svelte.scm @@ -0,0 +1,42 @@ +; inherits: html + +(raw_text) @none + +[ + "as" + "key" + "html" + "snippet" + "render" +] @keyword + +"const" @type.qualifier + +[ + "if" + "else" + "then" +] @keyword.conditional + +"each" @keyword.repeat + +[ + "await" + "then" +] @keyword.coroutine + +"catch" @keyword.exception + +"debug" @keyword.debug + +[ + "{" + "}" +] @punctuation.bracket + +[ + "#" + ":" + "/" + "@" +] @tag.delimiter diff --git a/app/assets/queries/swift.scm b/app/assets/queries/swift.scm new file mode 100644 index 00000000..98591cf1 --- /dev/null +++ b/app/assets/queries/swift.scm @@ -0,0 +1,62 @@ +[ + "if" "else" "guard" + "for" "while" "repeat" + "switch" "case" "default" "fallthrough" + "break" "continue" "return" "throw" + "do" "catch" "try" "defer" + "import" + "class" "struct" "enum" "protocol" "extension" "typealias" "associatedtype" + "func" "init" "deinit" "subscript" "operator" + "var" "let" + "static" "override" "mutating" "nonmutating" + "public" "private" "fileprivate" "internal" "open" + "final" "required" "convenience" "lazy" "weak" "unowned" + "async" "await" + "where" "in" "is" "as" + "self" "super" "Self" + "nil" + "true" "false" + "inout" "some" "any" + "get" "set" "willSet" "didSet" + "throws" "rethrows" + "indirect" + "precedencegroup" "infix" "prefix" "postfix" +] @keyword + +(function_declaration (identifier) @function) +(initializer_declaration "init" @function) + +(class_declaration (identifier) @type) +(struct_declaration (identifier) @type) +(enum_declaration (identifier) @type) +(protocol_declaration (identifier) @type) +(typealias_declaration (identifier) @type) +(type_identifier) @type +(standard_type) @type.builtin + +(string) @string +(static_string_literal) @string + +(number) @number +(boolean_literal) @boolean + +(identifier) @variable + +(modifier) @keyword + +[ + "(" ")" "[" "]" "{" "}" +] @punctuation.bracket + +[ + "," "." ":" ";" "->" "=>" +] @punctuation.delimiter + +[ + "=" "+" "-" "*" "/" "%" + "==" "!=" "<" ">" "<=" ">=" + "&&" "||" "!" + "&" "|" "^" "~" + "?" "??" + "..<" "..." +] @operator diff --git a/app/assets/queries/toml.scm b/app/assets/queries/toml.scm new file mode 100644 index 00000000..45614893 --- /dev/null +++ b/app/assets/queries/toml.scm @@ -0,0 +1,53 @@ +; Properties +;----------- + +(bare_key) @type + +(quoted_key) @string + +(pair + (bare_key)) @property + +(pair + (dotted_key + (bare_key) @property)) + +; Literals +;--------- + +(boolean) @boolean + +(comment) @comment + +(string) @string + +[ + (integer) + (float) +] @number + +[ + (offset_date_time) + (local_date_time) + (local_date) + (local_time) +] @string.special + +; Punctuation +;------------ + +[ + "." + "," +] @punctuation.delimiter + +"=" @operator + +[ + "[" + "]" + "[[" + "]]" + "{" + "}" +] @punctuation.bracket diff --git a/app/assets/queries/typescript.scm b/app/assets/queries/typescript.scm new file mode 100644 index 00000000..c758b516 --- /dev/null +++ b/app/assets/queries/typescript.scm @@ -0,0 +1,35 @@ +; Types + +(type_identifier) @type +(predefined_type) @type.builtin + +((identifier) @type + (#match? @type "^[A-Z]")) + +(type_arguments + "<" @punctuation.bracket + ">" @punctuation.bracket) + +; Variables + +(required_parameter (identifier) @variable.parameter) +(optional_parameter (identifier) @variable.parameter) + +; Keywords + +[ "abstract" + "declare" + "enum" + "export" + "implements" + "interface" + "keyof" + "namespace" + "private" + "protected" + "public" + "type" + "readonly" + "override" + "satisfies" +] @keyword diff --git a/app/assets/queries/vue.scm b/app/assets/queries/vue.scm new file mode 100644 index 00000000..70e4f405 --- /dev/null +++ b/app/assets/queries/vue.scm @@ -0,0 +1,54 @@ +; inherits: html_tags + +[ + "[" + "]" +] @punctuation.bracket + +[ + ":" + "." +] @character.special + +[ + (interpolation) + "@" +] @punctuation.special + +(interpolation + (raw_text) @none) + +(dynamic_directive_inner_value) @variable + +(directive_name) @tag.attribute + +; Accessing a component object's field +(":" + . + (directive_value) @variable.member) + +("." + . + (directive_value) @property) + +; @click is like onclick for HTML +("@" + . + (directive_value) @function.method) + +; Used in v-slot, declaring position the element should be put in +("#" + . + (directive_value) @variable) + +(directive_attribute + (quoted_attribute_value) @punctuation.special) + +(directive_attribute + (quoted_attribute_value + (attribute_value) @none)) + +(directive_modifier) @function.method + +((template_element) @_template + (#set! @_template bo.commentstring "")) diff --git a/app/assets/queries/wgsl.scm b/app/assets/queries/wgsl.scm new file mode 100644 index 00000000..7c72e9b4 --- /dev/null +++ b/app/assets/queries/wgsl.scm @@ -0,0 +1,111 @@ +(int_literal) @number +(float_literal) @float +(bool_literal) @boolean + +(type_declaration [ "bool" "u32" "i32" "f16" "f32" ] @type.builtin) +(type_declaration) @type + +(function_declaration + (identifier) @function) + +(parameter + (variable_identifier_declaration (identifier) @parameter)) + +(struct_declaration + (identifier) @structure) + +(struct_declaration + (struct_member (variable_identifier_declaration (identifier) @field))) + +(attribute + (identifier) @attribute) + +(identifier) @variable + +(type_constructor_or_function_call_expression + (type_declaration) @function.call) + +[ + "struct" + "bitcast" + "discard" + "enable" + "fallthrough" + "let" + "type" + "var" + "override" + (texel_format) +] @keyword + +[ + "private" + "storage" + "uniform" + "workgroup" +] @storageclass + +[ + "read" + "read_write" + "write" +] @type.qualifier + +"fn" @keyword.function + +"return" @keyword.return + +[ "," "." ":" ";" "->" ] @punctuation.delimiter + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket + +[ + "loop" + "for" + "while" + "break" + "continue" + "continuing" +] @repeat + +[ + "if" + "else" + "switch" + "case" + "default" +] @conditional + +[ + "&" + "&&" + "/" + "!" + "=" + "==" + "!=" + ">" + ">=" + ">>" + "<" + "<=" + "<<" + "%" + "-" + "+" + "|" + "||" + "*" + "~" + "^" + "@" + "++" + "--" +] @operator + +[ + (line_comment) + (block_comment) +] @comment + +(ERROR) @error diff --git a/app/assets/queries/xml.scm b/app/assets/queries/xml.scm new file mode 100644 index 00000000..9861eea1 --- /dev/null +++ b/app/assets/queries/xml.scm @@ -0,0 +1,168 @@ +;; XML declaration + +"xml" @keyword + +[ "version" "encoding" "standalone" ] @property + +(EncName) @string.special + +(VersionNum) @number + +[ "yes" "no" ] @boolean + +;; Processing instructions + +(PI) @embedded + +(PI (PITarget) @keyword) + +;; Element declaration + +(elementdecl + "ELEMENT" @keyword + (Name) @tag) + +(contentspec + (_ (Name) @property)) + +"#PCDATA" @type.builtin + +[ "EMPTY" "ANY" ] @string.special.symbol + +[ "*" "?" "+" ] @operator + +;; Entity declaration + +(GEDecl + "ENTITY" @keyword + (Name) @constant) + +(GEDecl (EntityValue) @string) + +(NDataDecl + "NDATA" @keyword + (Name) @label) + +;; Parsed entity declaration + +(PEDecl + "ENTITY" @keyword + "%" @operator + (Name) @constant) + +(PEDecl (EntityValue) @string) + +;; Notation declaration + +(NotationDecl + "NOTATION" @keyword + (Name) @constant) + +(NotationDecl + (ExternalID + (SystemLiteral (URI) @string.special))) + +;; Attlist declaration + +(AttlistDecl + "ATTLIST" @keyword + (Name) @tag) + +(AttDef (Name) @property) + +(AttDef (Enumeration (Nmtoken) @string)) + +(DefaultDecl (AttValue) @string) + +[ + (StringType) + (TokenizedType) +] @type.builtin + +(NotationType "NOTATION" @type.builtin) + +[ + "#REQUIRED" + "#IMPLIED" + "#FIXED" +] @attribute + +;; Entities + +(EntityRef) @constant + +((EntityRef) @constant.builtin + (#any-of? @constant.builtin + "&" "<" ">" """ "'")) + +(CharRef) @constant + +(PEReference) @constant + +;; External references + +[ "PUBLIC" "SYSTEM" ] @keyword + +(PubidLiteral) @string.special + +(SystemLiteral (URI) @markup.link) + +;; Processing instructions + +(XmlModelPI "xml-model" @keyword) + +(StyleSheetPI "xml-stylesheet" @keyword) + +(PseudoAtt (Name) @property) + +(PseudoAtt (PseudoAttValue) @string) + +;; Doctype declaration + +(doctypedecl "DOCTYPE" @keyword) + +(doctypedecl (Name) @type) + +;; Tags + +(STag (Name) @tag) + +(ETag (Name) @tag) + +(EmptyElemTag (Name) @tag) + +;; Attributes + +(Attribute (Name) @property) + +(Attribute (AttValue) @string) + +;; Delimiters & punctuation + +[ + "" + "" + "<" ">" + "" +] @punctuation.delimiter + +[ "(" ")" "[" "]" ] @punctuation.bracket + +[ "\"" "'" ] @punctuation.delimiter + +[ "," "|" "=" ] @operator + +;; Text + +(CharData) @markup + +(CDSect + (CDStart) @markup.heading + (CData) @markup.raw + "]]>" @markup.heading) + +;; Misc + +(Comment) @comment + +(ERROR) @error diff --git a/app/assets/queries/yaml.scm b/app/assets/queries/yaml.scm new file mode 100644 index 00000000..cb9dcc62 --- /dev/null +++ b/app/assets/queries/yaml.scm @@ -0,0 +1,79 @@ +(boolean_scalar) @boolean + +(null_scalar) @constant.builtin + +[ + (double_quote_scalar) + (single_quote_scalar) + (block_scalar) + (string_scalar) +] @string + +[ + (integer_scalar) + (float_scalar) +] @number + +(comment) @comment + +[ + (anchor_name) + (alias_name) +] @label + +(tag) @type + +[ + (yaml_directive) + (tag_directive) + (reserved_directive) +] @attribute + +(block_mapping_pair + key: (flow_node + [ + (double_quote_scalar) + (single_quote_scalar) + ] @property)) + +(block_mapping_pair + key: (flow_node + (plain_scalar + (string_scalar) @property))) + +(flow_mapping + (_ + key: (flow_node + [ + (double_quote_scalar) + (single_quote_scalar) + ] @property))) + +(flow_mapping + (_ + key: (flow_node + (plain_scalar + (string_scalar) @property)))) + +[ + "," + "-" + ":" + ">" + "?" + "|" +] @punctuation.delimiter + +[ + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "*" + "&" + "---" + "..." +] @punctuation.special diff --git a/app/assets/queries/zig.scm b/app/assets/queries/zig.scm new file mode 100644 index 00000000..1f900678 --- /dev/null +++ b/app/assets/queries/zig.scm @@ -0,0 +1,283 @@ +; Variables +(identifier) @variable + +; Parameters +(parameter + name: (identifier) @variable.parameter) + +(payload + (identifier) @variable.parameter) + +; Types +(parameter + type: (identifier) @type) + +((identifier) @type + (#lua-match? @type "^[A-Z_][a-zA-Z0-9_]*")) + +(variable_declaration + (identifier) @type + "=" + [ + (struct_declaration) + (enum_declaration) + (union_declaration) + (opaque_declaration) + ]) + +[ + (builtin_type) + "anyframe" +] @type.builtin + +; Constants +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]+$")) + +[ + "null" + "unreachable" + "undefined" +] @constant.builtin + +(field_expression + . + member: (identifier) @constant) + +(enum_declaration + (container_field + type: (identifier) @constant)) + +; Labels +(block_label + (identifier) @label) + +(break_label + (identifier) @label) + +; Fields +(field_initializer + . + (identifier) @variable.member) + +(field_expression + (_) + member: (identifier) @variable.member) + +(container_field + name: (identifier) @variable.member) + +(initializer_list + (assignment_expression + left: (field_expression + . + member: (identifier) @variable.member))) + +; Functions +(builtin_identifier) @function.builtin + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (field_expression + member: (identifier) @function.call)) + +(function_declaration + name: (identifier) @function) + +; Modules +(variable_declaration + (identifier) @module + (builtin_function + (builtin_identifier) @keyword.import + (#any-of? @keyword.import "@import" "@cImport"))) + +; Builtins +[ + "c" + "..." +] @variable.builtin + +((identifier) @variable.builtin + (#eq? @variable.builtin "_")) + +(calling_convention + (identifier) @variable.builtin) + +; Keywords +[ + "asm" + "defer" + "errdefer" + "test" + "error" + "const" + "var" +] @keyword + +[ + "struct" + "union" + "enum" + "opaque" +] @keyword.type + +[ + "async" + "await" + "suspend" + "nosuspend" + "resume" +] @keyword.coroutine + +"fn" @keyword.function + +[ + "and" + "or" + "orelse" +] @keyword.operator + +"return" @keyword.return + +[ + "if" + "else" + "switch" +] @keyword.conditional + +[ + "for" + "while" + "break" + "continue" +] @keyword.repeat + +[ + "usingnamespace" + "export" +] @keyword.import + +[ + "try" + "catch" +] @keyword.exception + +[ + "volatile" + "allowzero" + "noalias" + "addrspace" + "align" + "callconv" + "linksection" + "pub" + "inline" + "noinline" + "extern" + "comptime" + "packed" + "threadlocal" +] @keyword.modifier + +; Operator +[ + "=" + "*=" + "*%=" + "*|=" + "/=" + "%=" + "+=" + "+%=" + "+|=" + "-=" + "-%=" + "-|=" + "<<=" + "<<|=" + ">>=" + "&=" + "^=" + "|=" + "!" + "~" + "-" + "-%" + "&" + "==" + "!=" + ">" + ">=" + "<=" + "<" + "&" + "^" + "|" + "<<" + ">>" + "<<|" + "+" + "++" + "+%" + "-%" + "+|" + "-|" + "*" + "/" + "%" + "**" + "*%" + "*|" + "||" + ".*" + ".?" + "?" + ".." +] @operator + +; Literals +(character) @character + +([ + (string) + (multiline_string) +] @string + (#set! "priority" 95)) + +(integer) @number + +(float) @number.float + +(boolean) @boolean + +(escape_sequence) @string.escape + +; Punctuation +[ + "[" + "]" + "(" + ")" + "{" + "}" +] @punctuation.bracket + +[ + ";" + "." + "," + ":" + "=>" + "->" +] @punctuation.delimiter + +(payload + "|" @punctuation.bracket) + +; Comments +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^//!")) diff --git a/app/lib/builtin/editor/src/editor_view.dart b/app/lib/builtin/editor/src/editor_view.dart index 6e67c3a7..4b1d05a5 100644 --- a/app/lib/builtin/editor/src/editor_view.dart +++ b/app/lib/builtin/editor/src/editor_view.dart @@ -2,11 +2,13 @@ import 'dart:async'; import 'package:clide/clide.dart'; import 'package:clide_app/kernel/kernel.dart'; +import 'package:clide_app/kernel/src/syntax/tree_sitter_service.dart'; import 'package:clide_app/widgets/widgets.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'editor_controller.dart'; +import 'syntax_text_controller.dart'; /// Tier-2 editor tab. One tab — the content reflects the daemon's /// active buffer. Multi-file tabs live in the workspace-slot plan but @@ -25,14 +27,15 @@ class EditorView extends StatefulWidget { class _EditorViewState extends State { EditorController? _controller; - late final TextEditingController _text; + final TreeSitterService _syntax = TreeSitterService(); + late final SyntaxTextController _text; late final FocusNode _focus; String? _lastRemoteContent; @override void initState() { super.initState(); - _text = TextEditingController(); + _text = SyntaxTextController(syntax: _syntax); _focus = FocusNode(); _text.addListener(_onTextChanged); } @@ -54,11 +57,13 @@ class _EditorViewState extends State { _focus.dispose(); _controller?.removeListener(_onControllerChanged); _controller?.dispose(); + _syntax.dispose(); super.dispose(); } void _onControllerChanged() { final c = _controller!; + _text.updatePath(c.activePath); if (c.content != _lastRemoteContent) { _lastRemoteContent = c.content; final sel = TextSelection( @@ -110,6 +115,7 @@ class _EditorViewState extends State { Widget build(BuildContext context) { final c = _controller; final tokens = ClideTheme.of(context).surface; + _text.tokens = tokens; if (c == null) return const SizedBox.shrink(); return ListenableBuilder( diff --git a/app/lib/builtin/editor/src/syntax_text_controller.dart b/app/lib/builtin/editor/src/syntax_text_controller.dart new file mode 100644 index 00000000..e9c05e1c --- /dev/null +++ b/app/lib/builtin/editor/src/syntax_text_controller.dart @@ -0,0 +1,161 @@ +library; + +import 'dart:convert'; + +import 'package:clide_app/kernel/src/syntax/language_map.dart'; +import 'package:clide_app/kernel/src/syntax/tree_sitter_service.dart'; +import 'package:clide_app/kernel/src/theme/tokens.dart'; +import 'package:flutter/widgets.dart'; + +class SyntaxTextController extends TextEditingController { + SyntaxTextController({required TreeSitterService syntax}) + : _syntax = syntax; + + final TreeSitterService _syntax; + + String? _highlightedPath; + String? _highlightedText; + List _spans = const []; + SurfaceTokens? _tokens; + bool _highlighting = false; + + set tokens(SurfaceTokens value) => _tokens = value; + + void updatePath(String? path) { + if (path == _highlightedPath) return; + _highlightedPath = path; + _spans = const []; + _highlightedText = null; + _requestHighlight(); + } + + void _requestHighlight() { + final path = _highlightedPath; + final source = text; + if (path == null || source.isEmpty || _highlighting) return; + if (grammarForPath(path) == null) return; + if (source == _highlightedText) return; + + _highlighting = true; + _syntax.highlight(path, source).then((result) { + _highlighting = false; + if (text != source) { + _requestHighlight(); + return; + } + _highlightedText = source; + _spans = result.spans; + notifyListeners(); + }, onError: (_) { + _highlighting = false; + }); + } + + @override + set value(TextEditingValue newValue) { + super.value = newValue; + _requestHighlight(); + } + + @override + TextSpan buildTextSpan({ + required BuildContext context, + TextStyle? style, + required bool withComposing, + }) { + final tokens = _tokens; + if (_spans.isEmpty || tokens == null || text.isEmpty) { + return TextSpan(text: text, style: style); + } + + final source = text; + final sourceBytes = utf8.encode(source); + final children = []; + + // Convert byte offsets to character offsets. + // Build a byte-to-char map only up to the max byte we need. + final spans = _spans.where((s) => s.end <= sourceBytes.length).toList() + ..sort((a, b) => a.start != b.start ? a.start - b.start : a.end - b.end); + + if (spans.isEmpty) { + return TextSpan(text: text, style: style); + } + + int maxByte = 0; + for (final s in spans) { + if (s.end > maxByte) maxByte = s.end; + } + + // byte offset -> char offset lookup. + final byteToChar = List.filled(maxByte + 1, 0); + int charIdx = 0; + int byteIdx = 0; + while (byteIdx <= maxByte && charIdx <= source.length) { + byteToChar[byteIdx] = charIdx; + if (charIdx < source.length) { + final codeUnit = source.codeUnitAt(charIdx); + // UTF-16 surrogate pair = 4 bytes in UTF-8. + if (codeUnit >= 0xD800 && codeUnit <= 0xDBFF) { + final bytesForPair = utf8.encode(source.substring(charIdx, charIdx + 2)).length; + for (var b = 1; b < bytesForPair && byteIdx + b <= maxByte; b++) { + byteToChar[byteIdx + b] = charIdx; + } + byteIdx += bytesForPair; + charIdx += 2; + } else { + final bytesForChar = utf8.encode(source[charIdx]).length; + for (var b = 1; b < bytesForChar && byteIdx + b <= maxByte; b++) { + byteToChar[byteIdx + b] = charIdx; + } + byteIdx += bytesForChar; + charIdx++; + } + } else { + break; + } + } + + int charPos = 0; + for (final span in spans) { + if (span.start >= sourceBytes.length || span.end > sourceBytes.length) { + continue; + } + final spanCharStart = byteToChar[span.start]; + final spanCharEnd = span.end <= maxByte + ? byteToChar[span.end] + : source.length; + + if (spanCharStart < charPos) continue; + + // Gap before this span — plain text. + if (spanCharStart > charPos) { + children.add(TextSpan( + text: source.substring(charPos, spanCharStart), + style: style, + )); + } + + // The highlighted span. + if (spanCharEnd > spanCharStart) { + children.add(TextSpan( + text: source.substring(spanCharStart, spanCharEnd), + style: style?.copyWith( + color: TreeSitterService.colorForRole(span.role, tokens), + ), + )); + } + + charPos = spanCharEnd; + } + + // Trailing plain text. + if (charPos < source.length) { + children.add(TextSpan( + text: source.substring(charPos), + style: style, + )); + } + + return TextSpan(style: style, children: children); + } +} diff --git a/app/lib/kernel/src/syntax/language_map.dart b/app/lib/kernel/src/syntax/language_map.dart new file mode 100644 index 00000000..5d8bd4fa --- /dev/null +++ b/app/lib/kernel/src/syntax/language_map.dart @@ -0,0 +1,118 @@ +/// Maps file extensions and special filenames to grammar asset names. +library; + +String? grammarForPath(String path) { + final name = path.split('/').last; + final special = _filenameMap[name]; + if (special != null) return special; + + final dot = name.lastIndexOf('.'); + if (dot < 0) return null; + final ext = name.substring(dot).toLowerCase(); + return _extMap[ext]; +} + +const _filenameMap = { + 'Makefile': 'make', + 'makefile': 'make', + 'GNUmakefile': 'make', + 'Dockerfile': 'dockerfile', + 'dockerfile': 'dockerfile', + '.gitignore': 'gitignore', + '.gitconfig': 'git-config', + '.gitmodules': 'git-config', + 'justfile': 'just', + 'Justfile': 'just', +}; + +const _extMap = { + // Systems + '.c': 'c', + '.h': 'c', + '.cpp': 'cpp', + '.cxx': 'cpp', + '.cc': 'cpp', + '.hpp': 'cpp', + '.hxx': 'cpp', + '.cs': 'c-sharp', + + // Application + '.dart': 'dart', + '.go': 'go', + '.rs': 'rust', + '.java': 'java', + '.kt': 'kotlin', + '.kts': 'kotlin', + '.swift': 'swift', + '.rb': 'ruby', + '.py': 'python', + '.pyw': 'python', + '.ex': 'elixir', + '.exs': 'elixir', + '.erl': 'erlang', + '.hrl': 'erlang', + '.hs': 'haskell', + '.lhs': 'haskell', + '.jl': 'julia', + '.r': 'r', + '.R': 'r', + '.zig': 'zig', + '.nix': 'nix', + '.lua': 'lua', + '.php': 'php', + '.nkl': 'nickel', + + // Web + '.js': 'javascript', + '.mjs': 'javascript', + '.cjs': 'javascript', + '.jsx': 'javascript', + '.ts': 'typescript', + '.tsx': 'typescript', + '.html': 'html', + '.htm': 'html', + '.css': 'css', + '.svelte': 'svelte', + '.vue': 'vue', + + // Data / Config + '.json': 'json', + '.yaml': 'yaml', + '.yml': 'yaml', + '.toml': 'toml', + '.xml': 'xml', + '.svg': 'xml', + '.plist': 'xml', + '.hcl': 'hcl', + '.tf': 'hcl', + '.tfvars': 'hcl', + '.proto': 'proto', + '.regex': 'regex', + + // Markdown + '.md': 'markdown', + '.mdx': 'markdown', + '.markdown': 'markdown', + + // Shell + '.sh': 'bash', + '.bash': 'bash', + '.zsh': 'bash', + '.fish': 'bash', + + // Game dev + '.gd': 'gdscript', + '.glsl': 'glsl', + '.vert': 'glsl', + '.frag': 'glsl', + '.hlsl': 'hlsl', + '.wgsl': 'wgsl', + + // Data / Query + '.sql': 'sqlite', + '.sqlite': 'sqlite', + + // Other + '.diff': 'diff', + '.patch': 'diff', +}; diff --git a/app/lib/kernel/src/syntax/tree_sitter_ffi.dart b/app/lib/kernel/src/syntax/tree_sitter_ffi.dart new file mode 100644 index 00000000..ba8b804e --- /dev/null +++ b/app/lib/kernel/src/syntax/tree_sitter_ffi.dart @@ -0,0 +1,256 @@ +library; + +import 'dart:ffi'; +import 'dart:io' show File, Platform; + +import 'package:ffi/ffi.dart'; + +// -- Opaque handles ---------------------------------------------------------- + +final class TSParser extends Opaque {} +final class TSTree extends Opaque {} +final class TSQuery extends Opaque {} +final class TSQueryCursor extends Opaque {} +final class TSWasmStore extends Opaque {} +final class TSWasmEngine extends Opaque {} + +// -- Structs ----------------------------------------------------------------- + +final class TSNode extends Struct { + @Array(4) + external Array context; + external Pointer id; + external Pointer tree; +} + +final class TSQueryCapture extends Struct { + external TSNode node; + @Uint32() + external int index; +} + +final class TSQueryMatch extends Struct { + @Uint32() + external int id; + @Uint16() + external int patternIndex; + @Uint16() + external int captureCount; + external Pointer captures; +} + +final class TSWasmError extends Struct { + @Int32() + external int kind; + external Pointer message; +} + +// -- Native function typedefs ------------------------------------------------ + +// Parser +typedef _TsParserNew = Pointer Function(); +typedef _TsParserDelete = Void Function(Pointer); +typedef _TsParserSetLanguage = Bool Function(Pointer, Pointer); +typedef _TsParserSetWasmStore = Void Function( + Pointer, Pointer); +typedef _TsParserParseString = Pointer Function( + Pointer, Pointer, Pointer, Uint32); + +// Tree +typedef _TsTreeDelete = Void Function(Pointer); +typedef _TsTreeRootNode = TSNode Function(Pointer); + +// Node +typedef _TsNodeStartByte = Uint32 Function(TSNode); +typedef _TsNodeEndByte = Uint32 Function(TSNode); + +// Query +typedef _TsQueryNew = Pointer Function( + Pointer, Pointer, Uint32, Pointer, Pointer); +typedef _TsQueryDelete = Void Function(Pointer); +typedef _TsQueryCaptureCount = Uint32 Function(Pointer); +typedef _TsQueryCaptureNameForId = Pointer Function( + Pointer, Uint32, Pointer); + +// Query cursor +typedef _TsQueryCursorNew = Pointer Function(); +typedef _TsQueryCursorDelete = Void Function(Pointer); +typedef _TsQueryCursorExec = Void Function( + Pointer, Pointer, TSNode); +typedef _TsQueryCursorNextMatch = Bool Function( + Pointer, Pointer); + +// WASM store +typedef _TsWasmStoreNew = Pointer Function( + Pointer, Pointer); +typedef _TsWasmStoreDelete = Void Function(Pointer); +typedef _TsWasmStoreLoadLanguage = Pointer Function( + Pointer, Pointer, Pointer, Uint32, + Pointer); + +// WASM engine (from wasmtime C API, re-exported by tree-sitter) +typedef _WasmEngineNew = Pointer Function(); +typedef _WasmEngineDelete = Void Function(Pointer); + +// -- Dart function typedefs -------------------------------------------------- + +typedef DTsParserNew = Pointer Function(); +typedef DTsParserDelete = void Function(Pointer); +typedef DTsParserSetLanguage = bool Function(Pointer, Pointer); +typedef DTsParserSetWasmStore = void Function( + Pointer, Pointer); +typedef DTsParserParseString = Pointer Function( + Pointer, Pointer, Pointer, int); + +typedef DTsTreeDelete = void Function(Pointer); +typedef DTsTreeRootNode = TSNode Function(Pointer); + +typedef DTsNodeStartByte = int Function(TSNode); +typedef DTsNodeEndByte = int Function(TSNode); + +typedef DTsQueryNew = Pointer Function( + Pointer, Pointer, int, Pointer, Pointer); +typedef DTsQueryDelete = void Function(Pointer); +typedef DTsQueryCaptureCount = int Function(Pointer); +typedef DTsQueryCaptureNameForId = Pointer Function( + Pointer, int, Pointer); + +typedef DTsQueryCursorNew = Pointer Function(); +typedef DTsQueryCursorDelete = void Function(Pointer); +typedef DTsQueryCursorExec = void Function( + Pointer, Pointer, TSNode); +typedef DTsQueryCursorNextMatch = bool Function( + Pointer, Pointer); + +typedef DTsWasmStoreNew = Pointer Function( + Pointer, Pointer); +typedef DTsWasmStoreDelete = void Function(Pointer); +typedef DTsWasmStoreLoadLanguage = Pointer Function( + Pointer, Pointer, Pointer, int, + Pointer); + +typedef DWasmEngineNew = Pointer Function(); +typedef DWasmEngineDelete = void Function(Pointer); + +// -- Bindings ---------------------------------------------------------------- + +class TreeSitterLib { + TreeSitterLib._(DynamicLibrary lib) + : parserNew = lib.lookupFunction<_TsParserNew, DTsParserNew>( + 'ts_parser_new'), + parserDelete = lib.lookupFunction<_TsParserDelete, DTsParserDelete>( + 'ts_parser_delete'), + parserSetLanguage = + lib.lookupFunction<_TsParserSetLanguage, DTsParserSetLanguage>( + 'ts_parser_set_language'), + parserSetWasmStore = + lib.lookupFunction<_TsParserSetWasmStore, DTsParserSetWasmStore>( + 'ts_parser_set_wasm_store'), + parserParseString = + lib.lookupFunction<_TsParserParseString, DTsParserParseString>( + 'ts_parser_parse_string'), + treeDelete = lib.lookupFunction<_TsTreeDelete, DTsTreeDelete>( + 'ts_tree_delete'), + treeRootNode = lib.lookupFunction<_TsTreeRootNode, DTsTreeRootNode>( + 'ts_tree_root_node'), + nodeStartByte = lib.lookupFunction<_TsNodeStartByte, DTsNodeStartByte>( + 'ts_node_start_byte'), + nodeEndByte = lib.lookupFunction<_TsNodeEndByte, DTsNodeEndByte>( + 'ts_node_end_byte'), + queryNew = + lib.lookupFunction<_TsQueryNew, DTsQueryNew>('ts_query_new'), + queryDelete = lib.lookupFunction<_TsQueryDelete, DTsQueryDelete>( + 'ts_query_delete'), + queryCaptureCount = + lib.lookupFunction<_TsQueryCaptureCount, DTsQueryCaptureCount>( + 'ts_query_capture_count'), + queryCaptureNameForId = lib.lookupFunction<_TsQueryCaptureNameForId, + DTsQueryCaptureNameForId>('ts_query_capture_name_for_id'), + queryCursorNew = + lib.lookupFunction<_TsQueryCursorNew, DTsQueryCursorNew>( + 'ts_query_cursor_new'), + queryCursorDelete = + lib.lookupFunction<_TsQueryCursorDelete, DTsQueryCursorDelete>( + 'ts_query_cursor_delete'), + queryCursorExec = + lib.lookupFunction<_TsQueryCursorExec, DTsQueryCursorExec>( + 'ts_query_cursor_exec'), + queryCursorNextMatch = + lib.lookupFunction<_TsQueryCursorNextMatch, DTsQueryCursorNextMatch>( + 'ts_query_cursor_next_match'), + wasmStoreNew = lib.lookupFunction<_TsWasmStoreNew, DTsWasmStoreNew>( + 'ts_wasm_store_new'), + wasmStoreDelete = + lib.lookupFunction<_TsWasmStoreDelete, DTsWasmStoreDelete>( + 'ts_wasm_store_delete'), + wasmStoreLoadLanguage = lib.lookupFunction<_TsWasmStoreLoadLanguage, + DTsWasmStoreLoadLanguage>('ts_wasm_store_load_language'), + wasmEngineNew = lib.lookupFunction<_WasmEngineNew, DWasmEngineNew>( + 'wasm_engine_new'), + wasmEngineDelete = + lib.lookupFunction<_WasmEngineDelete, DWasmEngineDelete>( + 'wasm_engine_delete'); + + final DTsParserNew parserNew; + final DTsParserDelete parserDelete; + final DTsParserSetLanguage parserSetLanguage; + final DTsParserSetWasmStore parserSetWasmStore; + final DTsParserParseString parserParseString; + final DTsTreeDelete treeDelete; + final DTsTreeRootNode treeRootNode; + final DTsNodeStartByte nodeStartByte; + final DTsNodeEndByte nodeEndByte; + final DTsQueryNew queryNew; + final DTsQueryDelete queryDelete; + final DTsQueryCaptureCount queryCaptureCount; + final DTsQueryCaptureNameForId queryCaptureNameForId; + final DTsQueryCursorNew queryCursorNew; + final DTsQueryCursorDelete queryCursorDelete; + final DTsQueryCursorExec queryCursorExec; + final DTsQueryCursorNextMatch queryCursorNextMatch; + final DTsWasmStoreNew wasmStoreNew; + final DTsWasmStoreDelete wasmStoreDelete; + final DTsWasmStoreLoadLanguage wasmStoreLoadLanguage; + final DWasmEngineNew wasmEngineNew; + final DWasmEngineDelete wasmEngineDelete; + + static TreeSitterLib? _instance; + + static TreeSitterLib? get instance => _instance; + + static bool init() { + if (_instance != null) return true; + final lib = _openLibrary(); + if (lib == null) return false; + _instance = TreeSitterLib._(lib); + return true; + } + + static DynamicLibrary? _openLibrary() { + final libName = Platform.isLinux + ? 'libtree-sitter.so' + : Platform.isMacOS + ? 'libtree-sitter.dylib' + : Platform.isWindows + ? 'tree-sitter.dll' + : null; + if (libName == null) return null; + + // Try standard dlopen path first (works when lib is in bundle/lib/). + try { + return DynamicLibrary.open(libName); + } catch (_) {} + + // Try next to executable. + final exe = File(Platform.resolvedExecutable).parent.path; + for (final dir in ['$exe/lib', exe]) { + final path = '$dir/$libName'; + if (File(path).existsSync()) { + try { + return DynamicLibrary.open(path); + } catch (_) {} + } + } + return null; + } +} diff --git a/app/lib/kernel/src/syntax/tree_sitter_service.dart b/app/lib/kernel/src/syntax/tree_sitter_service.dart new file mode 100644 index 00000000..a1403e86 --- /dev/null +++ b/app/lib/kernel/src/syntax/tree_sitter_service.dart @@ -0,0 +1,291 @@ +library; + +import 'dart:convert' show utf8; +import 'dart:ffi'; +import 'dart:ui' show Color; + +import 'package:clide_app/kernel/src/syntax/language_map.dart'; +import 'package:clide_app/kernel/src/syntax/tree_sitter_ffi.dart'; +import 'package:clide_app/kernel/src/theme/tokens.dart'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart' show rootBundle; + +class SyntaxSpan { + const SyntaxSpan({ + required this.start, + required this.end, + required this.role, + }); + + final int start; + final int end; + final String role; +} + +class SyntaxResult { + const SyntaxResult(this.spans); + final List spans; + + static const empty = SyntaxResult([]); +} + +class _LoadedGrammar { + _LoadedGrammar({ + required this.language, + required this.query, + required this.captureNames, + }); + + final Pointer language; + final Pointer query; + final List captureNames; +} + +class TreeSitterService { + final Map _grammars = {}; + final Set _unavailable = {}; + + Pointer? _store; + Pointer? _parser; + Pointer? _cursor; + + bool _initDone = false; + + bool _init() { + if (_initDone) return _parser != null; + _initDone = true; + + final lib = TreeSitterLib.instance; + if (lib == null) return false; + + final engine = lib.wasmEngineNew(); + if (engine == nullptr) return false; + + final error = calloc(); + _store = lib.wasmStoreNew(engine, error); + lib.wasmEngineDelete(engine); + + if (_store == null || _store == nullptr) { + calloc.free(error); + return false; + } + calloc.free(error); + + _parser = lib.parserNew(); + if (_parser == null || _parser == nullptr) return false; + lib.parserSetWasmStore(_parser!, _store!); + + _cursor = lib.queryCursorNew(); + return true; + } + + Future<_LoadedGrammar?> _loadGrammar(String language) async { + if (_unavailable.contains(language)) return null; + final cached = _grammars[language]; + if (cached != null) return cached; + + if (!_init()) { + _unavailable.add(language); + return null; + } + + final lib = TreeSitterLib.instance!; + + try { + // Load grammar WASM bytes. + final wasmData = + await rootBundle.load('assets/grammars/$language.wasm'); + final wasmBytes = wasmData.buffer.asUint8List(); + + // Load into WASM store. + final nameNative = language.toNativeUtf8(); + final wasmNative = calloc(wasmBytes.length); + wasmNative.asTypedList(wasmBytes.length).setAll(0, wasmBytes); + final error = calloc(); + + final lang = lib.wasmStoreLoadLanguage( + _store!, nameNative.cast(), wasmNative, wasmBytes.length, error, + ); + + calloc.free(wasmNative); + calloc.free(nameNative); + + if (lang == nullptr) { + final msg = error.ref.message; + if (msg != nullptr) calloc.free(msg); + calloc.free(error); + _unavailable.add(language); + return null; + } + calloc.free(error); + + // Load highlight query. + String? querySource; + try { + querySource = + await rootBundle.loadString('assets/queries/$language.scm'); + } catch (_) {} + + Pointer query = nullptr; + List captureNames = []; + + if (querySource != null) { + final queryNative = querySource.toNativeUtf8(); + final queryLen = utf8.encode(querySource).length; + final errorOffset = calloc(); + final errorType = calloc(); + + query = lib.queryNew( + lang, queryNative.cast(), queryLen, errorOffset, errorType, + ); + + calloc.free(queryNative); + calloc.free(errorOffset); + calloc.free(errorType); + + if (query != nullptr) { + final count = lib.queryCaptureCount(query); + final lenOut = calloc(); + for (var i = 0; i < count; i++) { + final namePtr = lib.queryCaptureNameForId(query, i, lenOut); + final len = lenOut.value; + captureNames.add(namePtr.cast().toDartString(length: len)); + } + calloc.free(lenOut); + } + } + + final grammar = _LoadedGrammar( + language: lang, + query: query, + captureNames: captureNames, + ); + _grammars[language] = grammar; + return grammar; + } catch (_) { + _unavailable.add(language); + return null; + } + } + + Future hasGrammar(String path) async { + final lang = grammarForPath(path); + if (lang == null) return false; + return (await _loadGrammar(lang)) != null; + } + + Future languageFor(String path) async { + final lang = grammarForPath(path); + if (lang == null) return null; + return (await _loadGrammar(lang)) != null ? lang : null; + } + + List get loadedLanguages => _grammars.keys.toList(); + + Future highlight(String path, String source) async { + final lang = grammarForPath(path); + if (lang == null) return SyntaxResult.empty; + + final grammar = await _loadGrammar(lang); + if (grammar == null || grammar.query == nullptr) { + return SyntaxResult.empty; + } + + final lib = TreeSitterLib.instance!; + final parser = _parser!; + final cursor = _cursor!; + + // Set language on parser for this parse. + lib.parserSetLanguage(parser, grammar.language); + + // Parse source. + final sourceNative = source.toNativeUtf8(); + final sourceLen = utf8.encode(source).length; + final tree = lib.parserParseString( + parser, nullptr, sourceNative.cast(), sourceLen, + ); + + if (tree == nullptr) { + calloc.free(sourceNative); + return SyntaxResult.empty; + } + + final root = lib.treeRootNode(tree); + + // Run highlight query. + lib.queryCursorExec(cursor, grammar.query, root); + + final match = calloc(); + final spans = []; + + while (lib.queryCursorNextMatch(cursor, match)) { + final m = match.ref; + for (var i = 0; i < m.captureCount; i++) { + final cap = m.captures[i]; + final captureIndex = cap.index; + if (captureIndex < grammar.captureNames.length) { + spans.add(SyntaxSpan( + start: lib.nodeStartByte(cap.node), + end: lib.nodeEndByte(cap.node), + role: grammar.captureNames[captureIndex], + )); + } + } + } + + calloc.free(match); + lib.treeDelete(tree); + calloc.free(sourceNative); + + return SyntaxResult(spans); + } + + void dispose() { + final lib = TreeSitterLib.instance; + if (lib == null) return; + + for (final grammar in _grammars.values) { + if (grammar.query != nullptr) lib.queryDelete(grammar.query); + } + _grammars.clear(); + + if (_cursor != null && _cursor != nullptr) lib.queryCursorDelete(_cursor!); + // Parser and WASM store are cleaned up together — deleting the parser + // does not delete the store, but the store owns the languages. + if (_parser != null && _parser != nullptr) lib.parserDelete(_parser!); + if (_store != null && _store != nullptr) lib.wasmStoreDelete(_store!); + + _parser = null; + _store = null; + _cursor = null; + _unavailable.clear(); + } + + static Color colorForRole(String role, SurfaceTokens tokens) { + return switch (role) { + 'keyword' || 'repeat' || 'conditional' || 'include' || + 'exception' || 'operator' => + tokens.syntaxKeyword, + 'type' || 'type.builtin' || 'constructor' => tokens.syntaxType, + 'string' || 'string.special' => tokens.syntaxString, + 'number' || 'float' || 'boolean' => tokens.syntaxNumber, + 'comment' => tokens.syntaxComment, + 'function' || 'function.builtin' || 'function.method' || + 'method' => + tokens.syntaxMethod, + 'punctuation.bracket' || 'punctuation.delimiter' || + 'punctuation.special' => + tokens.syntaxPunct, + 'variable' || 'variable.builtin' || 'variable.parameter' => + tokens.globalForeground, + 'property' || 'field' => tokens.syntaxMethod, + 'constant' || 'constant.builtin' => tokens.syntaxNumber, + 'tag' || 'attribute' => tokens.syntaxKeyword, + 'namespace' || 'module' => tokens.syntaxType, + 'text.title' => tokens.syntaxKeyword, + 'text.literal' || 'text.reference' || 'text.uri' => tokens.syntaxString, + 'text.emphasis' || 'text.strong' => tokens.syntaxType, + _ => tokens.globalForeground, + }; + } +} diff --git a/app/lib/main.dart b/app/lib/main.dart index edf32cea..b49102fa 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -12,7 +12,6 @@ import 'package:clide_app/builtin/git/git.dart'; import 'package:clide_app/builtin/grammars_core/grammars_core.dart'; import 'package:clide_app/builtin/graph/graph.dart'; import 'package:clide_app/builtin/ipc_status/ipc_status.dart'; -import 'package:clide_app/builtin/jira/jira.dart'; import 'package:clide_app/builtin/keybindings_ui/keybindings_ui.dart'; import 'package:clide_app/builtin/markdown/markdown.dart'; import 'package:clide_app/builtin/pql/pql.dart'; @@ -26,6 +25,7 @@ import 'package:clide_app/builtin/welcome/welcome.dart'; import 'dart:io' show Directory, Platform; import 'package:clide_app/kernel/kernel.dart'; +import 'package:clide_app/kernel/src/syntax/tree_sitter_ffi.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/widgets.dart'; @@ -33,12 +33,10 @@ import 'package:flutter/widgets.dart'; Future main() async { final binding = WidgetsFlutterBinding.ensureInitialized(); - // Enable the semantics tree at boot so screen readers (Orca, VoiceOver, - // NVDA) + the headless-browser automation harness both work out of the - // box. Production release builds could gate this on a platform check, - // but a11y-first means always-on. binding.ensureSemantics(); + TreeSitterLib.init(); + final appDir = await _resolveAppDir(); final themes = await _loadBundledThemes(); @@ -73,7 +71,6 @@ Future main() async { ..register(PqlExtension()) ..register(TodosExtension()) ..register(ProblemsExtension()) - ..register(JiraExtension()) ..register(CanvasExtension()) ..register(GraphExtension()) ..register(SettingsUiExtension()) diff --git a/app/linux/CMakeLists.txt b/app/linux/CMakeLists.txt index 7ed744e9..0a7e6216 100644 --- a/app/linux/CMakeLists.txt +++ b/app/linux/CMakeLists.txt @@ -112,6 +112,12 @@ install(DIRECTORY "${NATIVE_ASSETS_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) +# Vendored native libraries (libtree-sitter with wasmtime embedded). +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../native/linux-x64/" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime + FILES_MATCHING PATTERN "*.so") + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/app/native/linux-x64/BUILD.md b/app/native/linux-x64/BUILD.md new file mode 100644 index 00000000..21cbd850 --- /dev/null +++ b/app/native/linux-x64/BUILD.md @@ -0,0 +1,49 @@ +# libtree-sitter.so — vendored build record + +## Status: PROVISIONAL — built locally, not yet CI + +Built on contributor machine. CI-based reproducible build required before release. + +## Source + +- **Repository**: https://github.com/tree-sitter/tree-sitter +- **Version**: 0.26.8 (tag v0.26.8) +- **Commit**: cd5b087cd9f45ca6d93ab1954f6b7c8534f324d2 +- **Clone**: `/var/mnt/data/projects/treesitter/tree-sitter/` + +## Build command + +```bash +cmake -B build \ + -DTREE_SITTER_FEATURE_WASM=ON \ + -DBUILD_SHARED_LIBS=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INCLUDE_PATH=/home/linuxbrew/.linuxbrew/include \ + -DWASMTIME_LIBRARY=/home/linuxbrew/.linuxbrew/lib/libwasmtime.a +cmake --build build -j$(nproc) +``` + +## Dependencies (statically linked) + +- **wasmtime 44.0.0** — Apache-2.0 with LLVM exception + - Source: Bytecode Alliance, installed via Homebrew + - Linked statically (`libwasmtime.a`) so we ship one `.so` + +## Toolchain + +- **Compiler**: GCC 15.2.1 (Fedora 43) +- **Target**: x86_64-linux-gnu +- **CMake**: system + +## Binary details + +- **Size**: ~24 MB +- **Exports**: `ts_wasm_store_new`, `ts_wasm_store_delete`, `ts_wasm_store_load_language`, `ts_wasm_store_language_count`, plus full tree-sitter C API + +## TODO before release + +- [ ] Build in CI from pinned source SHA +- [ ] Record SHA-256 of output +- [ ] Cross-compile for macOS (aarch64, x86_64) +- [ ] Cross-compile for Windows (x86_64) +- [ ] Vendor wasmtime NOTICE file per POLICY.md Apache-2.0 rules diff --git a/app/native/linux-x64/libtree-sitter.so b/app/native/linux-x64/libtree-sitter.so new file mode 100755 index 00000000..1a3aaf55 Binary files /dev/null and b/app/native/linux-x64/libtree-sitter.so differ diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 7f26726e..3e3eda9f 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -78,6 +78,9 @@ flutter: - assets/logo/clide-logo-256.png - assets/logo/clide-logo-512.png - assets/logo/clide-logo-1024.png + # Tree-sitter WASM grammars and highlight queries. + - assets/grammars/ + - assets/queries/ # Bundled fonts. OFL-licensed; license files live alongside each. # diff --git a/tools/ts-wasm/bridge.c b/tools/ts-wasm/bridge.c new file mode 100644 index 00000000..6de21b32 --- /dev/null +++ b/tools/ts-wasm/bridge.c @@ -0,0 +1,87 @@ +// Tree-sitter WASM bridge — compiled per grammar into a self-contained +// WASM module. Exports: init, set_query, parse_and_highlight, +// capture_count, capture_name, ts_alloc, ts_dealloc. + +#include "tree_sitter/api.h" +#include +#include + +// Linked at compile time via -DGRAMMAR_FN=tree_sitter_. +extern const TSLanguage *GRAMMAR_FN(void); + +static TSParser *g_parser = NULL; +static TSQuery *g_query = NULL; +static TSQueryCursor *g_cursor = NULL; + +// 12-byte packed span: start_byte, end_byte, capture_index. +typedef struct { uint32_t start; uint32_t end; uint32_t capture; } Span; + +__attribute__((export_name("init"))) +int32_t init(void) { + g_parser = ts_parser_new(); + if (!g_parser) return -1; + if (!ts_parser_set_language(g_parser, GRAMMAR_FN())) return -2; + g_cursor = ts_query_cursor_new(); + if (!g_cursor) return -3; + return 0; +} + +// Returns 0 on success, or (error_offset + 1) on failure. +__attribute__((export_name("set_query"))) +int32_t set_query(const char *src, uint32_t len) { + if (g_query) { ts_query_delete(g_query); g_query = NULL; } + uint32_t error_offset; + TSQueryError error_type; + g_query = ts_query_new(GRAMMAR_FN(), src, len, &error_offset, &error_type); + return g_query ? 0 : (int32_t)(error_offset + 1); +} + +// Parse source and run the active query. Writes Span structs into |out|. +// Returns the number of spans written. +__attribute__((export_name("parse_and_highlight"))) +uint32_t parse_and_highlight( + const char *src, uint32_t src_len, + Span *out, uint32_t max_spans +) { + if (!g_parser || !g_query || !g_cursor) return 0; + + TSTree *tree = ts_parser_parse_string(g_parser, NULL, src, src_len); + if (!tree) return 0; + + TSNode root = ts_tree_root_node(tree); + ts_query_cursor_exec(g_cursor, g_query, root); + + TSQueryMatch match; + uint32_t count = 0; + while (ts_query_cursor_next_match(g_cursor, &match) && count < max_spans) { + for (uint16_t i = 0; i < match.capture_count && count < max_spans; i++) { + TSQueryCapture cap = match.captures[i]; + out[count].start = ts_node_start_byte(cap.node); + out[count].end = ts_node_end_byte(cap.node); + out[count].capture = cap.index; + count++; + } + } + + ts_tree_delete(tree); + return count; +} + +__attribute__((export_name("capture_count"))) +uint32_t capture_count(void) { + return g_query ? ts_query_capture_count(g_query) : 0; +} + +// Returns a pointer into WASM linear memory. Caller reads |*out_len| +// bytes starting at the returned address. +__attribute__((export_name("capture_name"))) +const char *capture_name(uint32_t index, uint32_t *out_len) { + if (!g_query) { *out_len = 0; return NULL; } + return ts_query_capture_name_for_id(g_query, index, out_len); +} + +__attribute__((export_name("ts_alloc"))) +void *ts_alloc(uint32_t size) { return malloc(size); } + +__attribute__((export_name("ts_dealloc"))) +void ts_dealloc(void *ptr) { free(ptr); } diff --git a/tools/ts-wasm/build.sh b/tools/ts-wasm/build.sh new file mode 100755 index 00000000..49643cb5 --- /dev/null +++ b/tools/ts-wasm/build.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# Compile tree-sitter core + each grammar + bridge.c into self-contained +# WASM modules. Requires wasi-sdk (cached by `tree-sitter build --wasm`). +# +# Usage: +# ./tools/ts-wasm/build.sh # build all grammars +# ./tools/ts-wasm/build.sh dart rust # build specific grammars +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +WASI_SDK="${WASI_SDK:-$HOME/.cache/tree-sitter/wasi-sdk}" +TS_CORE="${TS_CORE:-/var/mnt/data/projects/treesitter/tree-sitter}" +GRAMMARS_ROOT="${GRAMMARS_ROOT:-/var/mnt/data/projects/treesitter}" +OUT_DIR="$REPO_ROOT/app/assets/grammars" +QUERIES_DIR="$REPO_ROOT/app/assets/queries" + +CC="$WASI_SDK/bin/clang" +CXX="$WASI_SDK/bin/clang++" +SYSROOT="$WASI_SDK/share/wasi-sysroot" + +CFLAGS=( + --sysroot="$SYSROOT" + --target=wasm32-wasip1 + -O2 -flto + -fno-exceptions + -I"$TS_CORE/lib/include" + -I"$TS_CORE/lib/src" + -DTREE_SITTER_HIDE_SYMBOLS +) + +LDFLAGS=( + -Wl,--no-entry + -Wl,--export=init + -Wl,--export=set_query + -Wl,--export=parse_and_highlight + -Wl,--export=capture_count + -Wl,--export=capture_name + -Wl,--export=ts_alloc + -Wl,--export=ts_dealloc + -Wl,--export=__heap_base + -Wl,--strip-all + -Wl,--gc-sections +) + +mkdir -p "$OUT_DIR" "$QUERIES_DIR" + +# Grammar source-dir resolution. Multi-grammar repos (markdown, php, +# typescript, xml) nest the actual grammar under a subdirectory. +grammar_src_dir() { + local name="$1" base="$GRAMMARS_ROOT/tree-sitter-$name" + # Multi-grammar repos: check for a nested dir matching the grammar name. + for sub in "$base/$name" "$base/tree-sitter-$name"; do + [ -f "$sub/src/parser.c" ] && echo "$sub" && return + done + # Fallback: repo root. + [ -f "$base/src/parser.c" ] && echo "$base" && return + echo >&2 "error: no parser.c for $name"; return 1 +} + +# Derive the C function name (tree_sitter_) from parser.c. +grammar_fn_name() { + grep -oP 'TSLanguage \*\K(tree_sitter_\w+)(?=\(void\))' "$1/src/parser.c" | head -1 +} + +# Find highlight queries, preferring the grammar's own queries/ dir. +copy_queries() { + local name="$1" src_dir="$2" + # Try grammar-specific queries first, then repo-level. + local q="" + for candidate in \ + "$src_dir/queries/highlights.scm" \ + "$GRAMMARS_ROOT/tree-sitter-$name/queries/highlights.scm" \ + "$GRAMMARS_ROOT/tree-sitter-$name/queries/$name/highlights.scm" \ + "$GRAMMARS_ROOT/tree-sitter-$name/queries-src/highlights.scm"; do + [ -f "$candidate" ] && q="$candidate" && break + done + [ -n "$q" ] && cp "$q" "$QUERIES_DIR/$name.scm" || true +} + +build_grammar() { + local name="$1" + local src_dir; src_dir="$(grammar_src_dir "$name")" || return 1 + local fn; fn="$(grammar_fn_name "$src_dir")" + [ -z "$fn" ] && echo >&2 "error: can't find function name for $name" && return 1 + + local c_sources=( "$SCRIPT_DIR/bridge.c" "$TS_CORE/lib/src/lib.c" "$src_dir/src/parser.c" ) + local cxx_sources=() + + # Add external scanner. + if [ -f "$src_dir/src/scanner.c" ]; then + c_sources+=( "$src_dir/src/scanner.c" ) + fi + if [ -f "$src_dir/src/scanner.cc" ]; then + cxx_sources+=( "$src_dir/src/scanner.cc" ) + fi + + local mem; mem="$(free -h | awk '/Mem:/{print $3" used / "$7" avail"}')" + echo " $name ($fn) [$mem]" + + local tmpdir; tmpdir="$(mktemp -d)" + trap "rm -rf '$tmpdir'" RETURN + + # Drop -flto for C++ grammars — wasm-ld LTO on C++ eats 20 GB+. + local compile_flags=("${CFLAGS[@]}") + if [ ${#cxx_sources[@]} -gt 0 ]; then + compile_flags=("${compile_flags[@]/-flto/}") + fi + + # Compile C sources. + local objs=() + local i=0 + for src in "${c_sources[@]}"; do + $CC "${compile_flags[@]}" -DGRAMMAR_FN="$fn" -I"$src_dir/src" \ + -c "$src" -o "$tmpdir/$i.o" 2>&1 + objs+=( "$tmpdir/$i.o" ) + i=$((i + 1)) + done + + # Compile C++ sources (if any). + for src in "${cxx_sources[@]}"; do + $CXX "${compile_flags[@]}" -DGRAMMAR_FN="$fn" -I"$src_dir/src" \ + -fno-rtti -c "$src" -o "$tmpdir/$i.o" 2>&1 + objs+=( "$tmpdir/$i.o" ) + i=$((i + 1)) + done + + # Link. + local linker="$CC" + [ ${#cxx_sources[@]} -gt 0 ] && linker="$CXX" + $linker "${compile_flags[@]}" "${objs[@]}" "${LDFLAGS[@]}" \ + -o "$OUT_DIR/$name.wasm" 2>&1 + + copy_queries "$name" "$src_dir" +} + +# Determine which grammars to build. +if [ $# -gt 0 ]; then + targets=("$@") +else + targets=() + for d in "$GRAMMARS_ROOT"/tree-sitter-*/; do + g="$(basename "$d" | sed 's/^tree-sitter-//')" + targets+=("$g") + done +fi + +echo "Building ${#targets[@]} grammars..." +failed=() +for g in "${targets[@]}"; do + if ! build_grammar "$g"; then + failed+=("$g") + fi +done + +echo "" +echo "Built $((${#targets[@]} - ${#failed[@]})) / ${#targets[@]} grammars." +if [ ${#failed[@]} -gt 0 ]; then + echo "Failed: ${failed[*]}" + exit 1 +fi