From 1eafd432d7cd500c77d7e433c7c7653f73629186 Mon Sep 17 00:00:00 2001 From: Emmanuel Briot Date: Thu, 8 Dec 2022 11:23:51 +0100 Subject: [PATCH] Add queries for neovim --- README.md | 115 +++++++++++++++++- grammar.js | 2 +- package.json | 13 ++- queries/folds.scm | 11 ++ queries/highlights.scm | 147 ++++++++++++++++++++++++ queries/locals.scm | 17 +++ queries/textobjects.scm | 19 +++ {corpus => test/corpus}/access.txt | 0 {corpus => test/corpus}/arrays.txt | 0 {corpus => test/corpus}/attributes.txt | 0 {corpus => test/corpus}/generics.txt | 0 {corpus => test/corpus}/packages.txt | 0 {corpus => test/corpus}/protected.txt | 0 {corpus => test/corpus}/records.txt | 0 {corpus => test/corpus}/renames.txt | 0 {corpus => test/corpus}/separates.txt | 0 {corpus => test/corpus}/statements.txt | 0 {corpus => test/corpus}/subprograms.txt | 0 {corpus => test/corpus}/tasking.txt | 0 {corpus => test/corpus}/types.txt | 0 20 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 queries/folds.scm create mode 100644 queries/highlights.scm create mode 100644 queries/locals.scm create mode 100644 queries/textobjects.scm rename {corpus => test/corpus}/access.txt (100%) rename {corpus => test/corpus}/arrays.txt (100%) rename {corpus => test/corpus}/attributes.txt (100%) rename {corpus => test/corpus}/generics.txt (100%) rename {corpus => test/corpus}/packages.txt (100%) rename {corpus => test/corpus}/protected.txt (100%) rename {corpus => test/corpus}/records.txt (100%) rename {corpus => test/corpus}/renames.txt (100%) rename {corpus => test/corpus}/separates.txt (100%) rename {corpus => test/corpus}/statements.txt (100%) rename {corpus => test/corpus}/subprograms.txt (100%) rename {corpus => test/corpus}/tasking.txt (100%) rename {corpus => test/corpus}/types.txt (100%) diff --git a/README.md b/README.md index 66d2692..9cf6b0a 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,122 @@ syntax, and slightly changed to reduce some conflicts. Tree-sitter doesn't need a full syntax tree, so we can take some shortcuts in the grammar. - ## Installation -Execute the following commands: +You will need neovim at least version 8.0 (not tested with earlier version). + +Installation is very manual at this stage, until we can integrate this package +inside nvim-treesitter itself. At the moment, assuming you are using lua +configuration and Packer for your package management: + +```lua +-- file: ~/.config/nvim/init.lua + +-- Merge this with any existing Packer configuration you might already +-- have. This loads packer itself, then loads the new `ada.nvim` package. +require('packer').startup(function(use) + use(require('mytreesitter.nvim')) +end) ``` + +Then create a new file to setup treesitter (or merge with an existing +configuration of course). +```lua +-- file: ~/.config/nvim/mytreesitter.nvim + +return { + 'nvim-treesitter/nvim-treesitter', + requires = { + 'nvim-treesitter/nvim-treesitter-textobjects' + }, + run=function() + require('nvim-treesitter.install').update({ with_sync = true }) + end, + config=function() + + -- Add support for our Ada parser + + local parsers = require "nvim-treesitter.parsers" + local parser_config = parsers.get_parser_configs() + parser_config.ada = { + install_info = { + url = "https://github.com/briot/tree-sitter-ada", + files = {"src/parser.c"}, + generate_requires_npm = false, + requires_generate_from_grammar = false, + }, + filetype = "ada", + } + end, +} +``` + +Finally, we need to install the Ada parser with: +```vim + :PackerSync " to install treesitter itself + :TSInstall ada " to install Ada support +``` + +However, the above part only installs the parser itself (to generate a syntax +tree from your source files). We now need to install queries, i.e. pattern +matching against that tree to provide various capabilities like syntax +highlighting, folding, indentation, smart textobject selection,... + +For this, and until we can merge with nvim-treesitter itself, you will have +to clone this github repository, then copy the `queries/` directory to +``` + ~/.local/share/nvim/site/pack/packer/start/nvim-treesitter/queries/ +``` + +## Usage + +### Syntax highlighting + +The above default configuration will replace the default regular +expressions-based syntax highlighting in vim. Instead, the highlighting is +based on the tree build every time you type something. + +The default highlighting looks pretty much like the one from the standard +Ada mode. However, the tree-based approach potentially opens the door for +smart highlighting, like "Use a different background color for a subprogram +specification", "show constant definitions in blue" or other high-level +approaches. + + WIP: document how users can do this in their own configuration files. + The current approach is to modify queries/highlights.scm + +Potentially (though it seems to be disabled in neovim at the moment), the +highlighting can also get smarter. Going back to the "show constants in +blue" example above, the queries/locals.scm file adds a simple approach so +that references to those constants can point to the definition, and therefore +use the same blue highlighting. + +Because neovim also has support for language servers (LSP), it is likely +better to rely on the language server here. + +### Block folding + +If you press za now, this will toggle the folding of the +"current block". +This is defined in queries/folds.scm, and currently knows about package +specifications, package bodies, subprograms bodies, if statements and loops. +Other semantic blocks could be added. + +### Smart Selection + +The file queries/textobjects.scm defines a function textobjects, so that +you can now use commands like + + - vaf (v)isually select (a) (f)unction or subprogram + - vif (v)isually select (i)nside a (f)unction or subprogram + - vai (v)isually select (a) (i)f statement (or loop) + - vii (v)isually select (i)nside an (i)f statement (or loop) + + +## Development + +Execute the following commands: +```bash npm install npm run test ``` diff --git a/grammar.js b/grammar.js index 42208b1..fe0e1f0 100644 --- a/grammar.js +++ b/grammar.js @@ -360,7 +360,7 @@ module.exports = grammar({ package_body: $ => seq( reservedWord('package'), reservedWord('body'), - $.name, + field('name', $.name), optional($.aspect_specification), reservedWord('is'), optional($.non_empty_declarative_part), diff --git a/package.json b/package.json index b5f5e53..f60310d 100644 --- a/package.json +++ b/package.json @@ -12,5 +12,16 @@ "testquick": "time tree-sitter test", "update-test": "tree-sitter test --update" }, - "main": "bindings/node" + "main": "bindings/node", + "tree-sitter": [ + { + "scope": "source.ada", + "file-types": [ + "ada" + ], + "highlights": [ + "queries/highlights.scm" + ] + } + ] } diff --git a/queries/folds.scm b/queries/folds.scm new file mode 100644 index 0000000..8d7e7e9 --- /dev/null +++ b/queries/folds.scm @@ -0,0 +1,11 @@ +;; Support for folding in Ada +;; za toggles folding a package, subprogram, if statement or loop + +[ + (package_specification) + (package_body) + (subprogram_body) + (block_statement) + (if_statement) + (loop_statement) +] @fold diff --git a/queries/highlights.scm b/queries/highlights.scm new file mode 100644 index 0000000..a03fd66 --- /dev/null +++ b/queries/highlights.scm @@ -0,0 +1,147 @@ +;; highlight queries. +;; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries +;; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations +;; for a list of recommended @ tags, though not all of them have matching +;; highlights in neovim. + +[ + "abort" + "abs" + "accept" + "all" + "at" + "begin" + "declare" + "delay" + "until" + "do" + "end" + "entry" + "exit" + "generic" + "is" + "null" + "others" + "out" + "pragma" + "renames" + "when" +] @keyword +[ + "abstract" + "access" + "aliased" + "array" + "constant" + "delta" + "digits" + "interface" + "limited" + "of" + "private" + "range" + "synchronized" + "tagged" +] @StorageClass +[ + "mod" + "new" + "protected" + "record" + "subtype" + "task" + "type" +] @type.definition +[ + "with" + "use" +] @include +[ + "body" + "function" + "overriding" + "procedure" + "package" + "separate" +] @keyword.function +[ + "and" + "in" + "not" + "or" + "xor" +] @keyword.operator +[ + "while" + "loop" + "for" + "parallel" + "reverse" + "some" +] @keyword.repeat +[ + "return" +] @keyword.return +[ + "case" + "if" + "else" + "then" + "elsif" + "select" +] @conditional +[ + "exception" + "raise" +] @exception +(comment) @comment +(comment) @spell ;; spell-check comments +(string_literal) @string +(string_literal) @spell ;; spell-check strings +(identifier) @variable +(numeric_literal) @number + +;; Highlight the name of subprograms +(procedure_specification + (name) @function +) +(function_specification + (name) @function +) +(package_specification + name: (name) @function ;; Should use @module, but no default highlight +) +(package_body + (name) @function ;; Should use @module, but no default highlight +) +(generic_instantiation + . (name) @function +) + +;; Change keyword categories inside type definitions. +;; WIP: waiting for simplified tree. + ; [ + ; "is" + ; "abstract" + ; "access" + ; "array" + ; "tagged" + ; "constant" + ; "range" + ; "mod" + ; "digits" + ; "delta" + ; "limited" + ; "synchronized" + ; ]* @keyword.type +(full_type_declaration + (identifier) @type + "is" @type.definition + ; (access_type_definition "access" @keyword.type) +) + +;; Highlight errors in red. This is not very useful in practice, as text will +;; be highlighted as user types, and the error could be elsewhere in the code. +;; This also requires defining :hi @error guifg=Red for instance. +(ERROR) @error + diff --git a/queries/locals.scm b/queries/locals.scm new file mode 100644 index 0000000..655e74b --- /dev/null +++ b/queries/locals.scm @@ -0,0 +1,17 @@ +;; Better highlighting by referencing to the definition, for variable +;; references. However, this is not yet supported by neovim +;; See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables + +(package_specification) @scope +(subprogram_specification) @scope +(block_statement) @scope + +(procedure_specification (name) @definition.var) +(function_specification (name) @definition.var) +(package_specification name: (name) @definition.var) +(package_body (name) @definition.var) +(generic_instantiation . (name) @definition.var) +(defining_identifier_list (identifier) @definition.var) + +(identifier) @reference +(name) @reference diff --git a/queries/textobjects.scm b/queries/textobjects.scm new file mode 100644 index 0000000..4771501 --- /dev/null +++ b/queries/textobjects.scm @@ -0,0 +1,19 @@ +;; Support for high-level text objects selections. +;; For instance: +;; vaf (v)isually select (a) (f)unction or subprogram +;; vif (v)isually select (i)nside a (f)unction or subprogram +;; vai (v)isually select (a) (i)f statement (or loop) +;; vii (v)isually select (i)nside an (i)f statement (or loop) +;; +;; https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/master/README.md + +(subprogram_body) @function.outer +(subprogram_body (non_empty_declarative_part) @function.inner) +(subprogram_body (handled_sequence_of_statements) @function.inner) +(subprogram_specification) @function.outer +(package_specification) @function.outer +(package_body) @function.outer +(if_statement) @block.outer +(if_statement (sequence_of_statements) @block.inner) +(loop_statement) @block.outer +(loop_statement (sequence_of_statements) @block.inner) diff --git a/corpus/access.txt b/test/corpus/access.txt similarity index 100% rename from corpus/access.txt rename to test/corpus/access.txt diff --git a/corpus/arrays.txt b/test/corpus/arrays.txt similarity index 100% rename from corpus/arrays.txt rename to test/corpus/arrays.txt diff --git a/corpus/attributes.txt b/test/corpus/attributes.txt similarity index 100% rename from corpus/attributes.txt rename to test/corpus/attributes.txt diff --git a/corpus/generics.txt b/test/corpus/generics.txt similarity index 100% rename from corpus/generics.txt rename to test/corpus/generics.txt diff --git a/corpus/packages.txt b/test/corpus/packages.txt similarity index 100% rename from corpus/packages.txt rename to test/corpus/packages.txt diff --git a/corpus/protected.txt b/test/corpus/protected.txt similarity index 100% rename from corpus/protected.txt rename to test/corpus/protected.txt diff --git a/corpus/records.txt b/test/corpus/records.txt similarity index 100% rename from corpus/records.txt rename to test/corpus/records.txt diff --git a/corpus/renames.txt b/test/corpus/renames.txt similarity index 100% rename from corpus/renames.txt rename to test/corpus/renames.txt diff --git a/corpus/separates.txt b/test/corpus/separates.txt similarity index 100% rename from corpus/separates.txt rename to test/corpus/separates.txt diff --git a/corpus/statements.txt b/test/corpus/statements.txt similarity index 100% rename from corpus/statements.txt rename to test/corpus/statements.txt diff --git a/corpus/subprograms.txt b/test/corpus/subprograms.txt similarity index 100% rename from corpus/subprograms.txt rename to test/corpus/subprograms.txt diff --git a/corpus/tasking.txt b/test/corpus/tasking.txt similarity index 100% rename from corpus/tasking.txt rename to test/corpus/tasking.txt diff --git a/corpus/types.txt b/test/corpus/types.txt similarity index 100% rename from corpus/types.txt rename to test/corpus/types.txt