diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 2a01ec376..18570695c 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -189,6 +189,7 @@ | sml | ✓ | | | | | snakemake | ✓ | | ✓ | `pylsp` | | solidity | ✓ | ✓ | | `solc` | +| spade | ✓ | | ✓ | `spade-language-server` | | spicedb | ✓ | | | | | sql | ✓ | ✓ | | | | sshclientconfig | ✓ | | | | diff --git a/languages.toml b/languages.toml index c92b78932..3c96d49fe 100644 --- a/languages.toml +++ b/languages.toml @@ -94,6 +94,7 @@ slint-lsp = { command = "slint-lsp", args = [] } solargraph = { command = "solargraph", args = ["stdio"] } solc = { command = "solc", args = ["--lsp"] } sourcekit-lsp = { command = "sourcekit-lsp" } +spade-language-server = {command = "spade-language-server"} svlangserver = { command = "svlangserver", args = [] } swipl = { command = "swipl", args = [ "-g", "use_module(library(lsp_server))", "-g", "lsp_server:main", "-t", "halt", "--", "stdio" ] } superhtml = { command = "superhtml", args = ["lsp"]} @@ -3876,3 +3877,28 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "cylc" source = { git = "https://github.com/elliotfontaine/tree-sitter-cylc", rev = "30dd40d9bf23912e4aefa93eeb4c7090bda3d0f6" } + +[[language]] +name = "spade" +scope = "source.spade" +roots = ["swim.toml"] +file-types = ['spade'] +injection-regex = "spade" +comment-tokens = ["//", "///"] +block-comment-tokens = [ + { start = "/*", end = "*/" }, + { start = "/**", end = "*/" }, +] +language-servers = [ "spade-language-server" ] +indent = { tab-width = 4, unit = " " } + +[language.auto-pairs] +'(' = ')' +'{' = '}' +'[' = ']' +'"' = '"' +'<' = '>' + +[[grammar]] +name = "spade" +source = { git = "https://gitlab.com/spade-lang/tree-sitter-spade/", rev = "4d5b141017c61fe7e168e0a5c5721ee62b0d9572" } diff --git a/pr.md b/pr.md new file mode 100644 index 000000000..0ea65a5ad --- /dev/null +++ b/pr.md @@ -0,0 +1,24 @@ +Syntax symbol pickers +== + +This adds two new symbol picker commands that use tree-sitter rather than LSP. We run a new `symbols.scm` query across the file and extract tagged things like function definitions, types, classes, etc. For languages with unambiguous syntax this behaves roughly the same as the LSP symbol picker (`s`). It's less precise though since we don't have semantic info about the language. For example it can easily produce false positives for C/C++ because of preprocessor magic. + +The hope is to start introducing LSP-like features for navigation that can work without installing or running a language server. I made these two pickers in particular because I don't like LSP equivalents in ErlangLS - the document symbol picker can take a long time to show up during boot and the workspace symbol picker only searches for module names. The other motivation is to have some navigation features in cases when running a language server is too cumbersome - either to set up or because of resource constraints. For example `clangd` needs a fair amount of setup (`compile_commands.json`) that you might not want to do when quickly reading through a codebase. + +GitHub already uses tree-sitter like this to provide [imprecise code navigation](https://docs.github.com/en/repositories/working-with-files/using-files/navigating-code-on-github#about-navigating-code-on-github). It should be possible to find definitions and references as well like `gd` and `gr` - this is left as a follow-up. + +This PR also adds commands that either open the LSP symbol picker or the syntax one if a language server is not available. This way you can customize a language to not use the LSP symbol pickers, for example: + +```toml +[[language]] +name = "erlang" +language-servers = [{ name = "erlang-ls", except-features = ["document-symbols", "workspace-symbols"] }] +``` + +and `s` will use the syntax symbol picker, while `s` on a Rust file will still prefer the language server. + +--- + +Outstanding question - how closely should we try to match LSP symbol kind? Not at all? Should we have markup specific symbol kinds? (For example see markdown's `symbols.scm`). + +Also this PR needs docs on writing `symbols.scm` queries. diff --git a/runtime/queries/spade/highlights.scm b/runtime/queries/spade/highlights.scm new file mode 100644 index 000000000..08511b859 --- /dev/null +++ b/runtime/queries/spade/highlights.scm @@ -0,0 +1,130 @@ +(self) @variable.builtin + +(unit_definition (identifier) @function) + +(parameter (identifier) @variable.parameter) + +((pipeline_reg_marker) @keyword) + +(scoped_identifier + path: (identifier) @namespace) +(scoped_identifier + (scoped_identifier + name: (identifier) @namespace)) + +((builtin_type) @type.builtin) + +((identifier) @type.builtin + (#any-of? + @type.builtin + "uint" + "Option" + "Memory")) + +((identifier) @type.enum.variant.builtin + (#any-of? @type.enum.variant.builtin "Some" "None")) + +((pipeline_stage_name) @label) + +((stage_reference + stage: (identifier) @label)) + +[ + "pipeline" + "let" + "set" + "entity" + "fn" + "reg" + "reset" + "initial" + "inst" + "assert" + "struct" + "enum" + "stage" + "impl" + "port" + "decl" + "mod" + "where" + "trait" +] @keyword + +[ + "use" +] @keyword.import + +[ + "$if" + "$else" + "$config" +] @keyword.directive + +((comptime_if ["{" "}"] @keyword.directive)) +((comptime_else ["{" "}"] @keyword.directive)) + +((attribute) ["#" "[" "]"] @punctuation.delimiter) + +[ + "else" + "if" + "match" +] @keyword.control.conditional + +(bool_literal) @constant.builtin.boolean +(int_literal) @constant.numeric.integer + +[ + "&" + "inv" + "-" + "=>" + ">" + "<" + "::<" + "::$<" + "=" + "->" + "~" + "!" +] @operator + + +((op_add) @operator) +((op_sub) @operator) +((op_mul) @operator) +((op_equals) @operator) +((op_lt) @operator) +((op_gt) @operator) +((op_le) @operator) +((op_ge) @operator) +((op_lshift) @operator) +((op_rshift) @operator) +((op_bitwise_and) @operator) +((op_bitwise_xor) @operator) +((op_bitwise_or) @operator) +((op_logical_and) @operator) +((op_logical_or) @operator) + + +[ + (line_comment) + (block_comment) +] @comment + +[ + (doc_comment) +] @comment.block.documentation + + +((identifier) @type + (#match? @type "[A-Z]")) + +((scoped_identifier + name: (identifier) @type) + (#match? @type "^[A-Z]")) + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + diff --git a/runtime/queries/spade/indents.scm b/runtime/queries/spade/indents.scm new file mode 100644 index 000000000..dc30bdc3e --- /dev/null +++ b/runtime/queries/spade/indents.scm @@ -0,0 +1,27 @@ + +[ + (unit_definition) + (struct_definition) + (enum_definition) + (enum_member) + (impl) + (mod) + (argument_list) + (let_binding) + (block) + (tuple_literal) + (array_literal) + (paren_expression) + (turbofish) + (generic_parameters) + (named_unpack) + (positional_unpack) + (tuple_pattern) +] @indent + +[ + "}" + "]" + ")" +] @outdent +