diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 06596fe11..8290ee650 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -20,10 +20,15 @@ Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
-**Output of `:checkhealth nvim_treesitter` ***
-```
+**Output of `:checkhealth nvim_treesitter`**
+
+
+
+
Paste the output here
-```
+
+
+
**Output of `nvim --version`**
```
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..7ac6d5b04
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+doc/tags
+.luacheckcache
diff --git a/.luacheckrc b/.luacheckrc
new file mode 100644
index 000000000..cb61432f9
--- /dev/null
+++ b/.luacheckrc
@@ -0,0 +1,16 @@
+-- Rerun tests only if their modification time changed.
+cache = true
+
+-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html
+ignore = {
+ "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off.
+ "411", -- Redefining a local variable.
+ "412", -- Redefining an argument.
+ "422", -- Shadowing an argument
+ "122" -- Indirectly setting a readonly global
+}
+
+-- Global objects defined by the C code
+read_globals = {
+ "vim",
+}
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..a3d4dfe86
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+
+language: c
+dist: bionic
+before_install:
+ - sudo apt-get update
+ - sudo add-apt-repository universe
+ - sudo apt install luarocks -y
+ - sudo luarocks install luacheck
+
+script:
+ - ./scripts/style-check.sh
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5a8c74ee6..47dcb9f83 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,6 +11,18 @@ As you know, `nvim-treesitter` is roughly splitted in two parts :
Depending on which part of the plugin you want to contribute to, please read the appropriate section.
+## Style Checks and Tests
+
+We haven't implemented any functionality tests yet. Feel free to contribute.
+However, we check code style with `luacheck`!
+Please install luacheck and activate our `pre-push` hook to automatically check style before
+every push:
+
+```bash
+luarocks install luacheck
+ln -s ../../scripts/pre-push .git/hooks/pre-push
+```
+
## Parser configurations
Contributing to parser configurations is basically modifying one of the `queries/*/*.scm`.
@@ -29,6 +41,7 @@ Here are some global advices :
- If not, you should consider installing the [tree-sitter cli](https://github.com/tree-sitter/tree-sitter/tree/master/cli),
you should then be able to open a local playground using `tree-sitter build-wasm && tree-sitter web-ui` within the
parsers repo.
+ - An Example of somewhat complex highlight queries can be found in queries/ruby/highlights.scm (Maintained by @TravonteD)
### Highlights
@@ -36,65 +49,75 @@ As languages differ quite a lot, here is a set of captures available to you when
One important thing to note is that many of these capture groups are not supported by `neovim` for now, and will not have any
effect on highlighting. We will work on improving highlighting in the near future though.
+
#### Misc
-`@comment`
-`@error` for error `(ERROR)` nodes.
-`@punctuation.delimiter` for `;` `.` `,`
-`@punctuation.bracket` for `()` or `{}`
+```
+@comment
+@error for error (ERROR` nodes.
+@punctuation.delimiter for `;` `.` `,`
+@punctuation.bracket for `()` or `{}`
+```
Some captures are related to language injection (like markdown code blocks). As this is not supported by neovim yet, these
are optional and will not have any effect for now.
-`@embedded`
-`@injection`
- `language`
- `content`
-
+```
+@embedded
+@injection
+ language
+ content
+```
#### Constants
-`@constant`
- `builtin`
- `macro`
-`@string`
- `regex`
- `escape`
-`@character`
-`@number`
-`@boolean`
-`@float`
+```
+@constant
+ builtin
+ macro
+@string
+ regex
+ escape
+@character
+@number
+@boolean
+@float
+```
#### Functions
-`@function`
- `builtin`
- `macro`
-`@parameter`
+```
+@function
+ builtin
+ macro
+@parameter
-`@method`
-`@field` or `@property`
+@method
+@field or @property
-`@constructor`
+@constructor
+```
#### Keywords
-`@conditional`
-`@repeat`
-`@label` for C/Lua-like labels
-`@operator`
-`@keyword`
-`@exception`
-`@include` keywords for including modules (e.g. import/from in Python)
-
-`@type`
- `builtin`
-`@structure`
+```
+@conditional
+@repeat
+@label for C/Lua-like labels
+@operator
+@keyword
+@exception
+@include keywords for including modules (e.g. import/from in Python)
+@type
+ builtin
+@structure
+```
### Locals
-
-`@definition` for various definitions
- `function`
- `method`
- `var`
- `macro`
- `type`
- `field`
- `doc` for documentation adjecent to a definition. E.g.
+```
+@definition for various definitions
+ function
+ method
+ var
+ macro
+ type
+ field
+ doc for documentation adjecent to a definition. E.g.
+```
```scheme
(comment)* @definition.doc
@@ -102,7 +125,8 @@ are optional and will not have any effect for now.
name: (field_identifier) @definition.method)
```
-`@scope`
-
-`@reference`
+```
+@scope
+@reference
+```
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index 233487d7d..5499de391 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,11 @@
[](https://gitter.im/nvim-treesitter/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+[](https://travis-ci.com/nvim-treesitter/nvim-treesitter)
# nvim-treesitter
Treesitter configurations and abstraction layer for Neovim.
+**Warning: Treesitter and Treesitter highlighting are an experimental feature of nightly versions of Neovim.
+Please consider the experience with this plug-in as experimental until Neovim 0.5 is released!**
+
# Quickstart
## Requirements
@@ -33,7 +37,7 @@ $ git clone https://github.com/nvim-treesitter/nvim-treesitter.git
Treesitter is using a different _parser_ for every language. It can be quite a pain to install, but fortunately `nvim-treesitter`
provides two command to tackle this issue:
- - `TSInstall` to install a given parser.
+ - `TSInstall` to install one or more parser. You can use `TSInstall all` to download all parsers.
- `TSInstallInfo` to know which parser is installed.
Let's say you need parsers for `lua`, `c`, and `python`, this is how you do with these commands:
@@ -99,9 +103,23 @@ require'nvim-treesitter.configs'.setup {
init_selection = 'gnn', -- maps in normal mode to init the node/scope selection
node_incremental = "grn", -- increment to the upper named parent
scope_incremental = "grc", -- increment to the upper scope (as defined in locals.scm)
- node_decremental = "grm", -- decrement to the previous node
+ node_decremental = "grm", -- decrement to the previous node
}
},
+ refactor = {
+ highlight_defintions = {
+ enable = true
+ },
+ smart_rename = {
+ enable = true,
+ smart_rename = "grr" -- mapping to rename reference under cursor
+ },
+ navigation = {
+ enable = true,
+ goto_definition = "gnd", -- mapping to go to definition of symbol under cursor
+ list_definitions = "gnD" -- mapping to list all definitions in current file
+ }
+ },
ensure_installed = 'all' -- one of 'all', 'language', or a list of languages
}
EOF
@@ -131,19 +149,23 @@ Some of these features are :
You can find the roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/projects/1).
The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!
-## Api
+## Available Modules
-Nvim-treesitter exposes an api to extend node capabilites. You can retrieve the api like this:
+- `highlight`: Consistent syntax highlighting.
+- `incremental_selection`: Syntax based selection.
+- `refactor.highlight_definitions`: Syntax based definition and usage highlighting.
+- `refactor.smart_rename`: Syntax based definition and usage renaming.
+- `refactor.navigation`: Syntax based definition listing and navigation.
+ * List all definitions
+ * Go to definition
+
+## Utils
+
+you can get some utility functions with
```lua
-local ts_node_api = require 'nvim-treesitter'.get_node_api()
+local ts_utils = require 'nvim-treesitter.ts_utils'
```
-
-You can also retrieve the current state of the current buffer with:
-```lua
-local buf_state = require'nvim-treesitter'.get_buf_state()
-```
-
-More information is available in neovim documentation (`:help nvim-treesitter-api`).
+More information is available in the help file (`:help nvim-treesitter-utils`).
## Supported Languages
@@ -156,22 +178,22 @@ List of currently supported languages:
- [x] ruby (maintained by @TravonteD)
- [x] c (maintained by @vigoux)
- [x] go (maintained by @theHamsta)
-- [ ] cpp
+- [x] cpp (maintained by @theHamsta, extends C queries)
- [ ] rust
- [x] python (maintained by @theHamsta)
-- [ ] javascript
-- [ ] typescript
+- [x] javascript (maintained by @steelsojka)
+- [x] typescript (maintained by @steelsojka)
- [ ] tsx
-- [ ] json
+- [x] json (maintained by @steelsojka)
- [x] html (maintained by @TravonteD)
- [ ] csharp
- [ ] swift
-- [ ] java
+- [x] java
- [ ] ocaml
- [x] css (maintained by @TravonteD)
- [ ] julia
- [ ] php
-- [ ] bash
+- [x] bash (maintained by @TravonteD)
- [ ] scala
- [ ] haskell
- [ ] toml
@@ -182,6 +204,13 @@ List of currently supported languages:
- [ ] markdown
- [x] regex (maintained by @theHamsta)
+## User Query Extensions
+
+You can add your own query files by placing a query file in vim's runtime path after `nvim-treesitter` is sourced.
+If the language has a built in query file, that file will be appended to or it will be used (useful for languages not yet supported).
+For example, you can add files to `/after/queries/lua/highlights.scm` to add more queries to lua highlights.
+You can also manually add query paths to the runtime path by adding this to your vim config `set rtp+='path/to/queries'`.
+
## Troubleshooting
Before doing anything run `:checkhealth nvim_treesitter`. This will help you find where the bug might come from.
diff --git a/doc/nvim-treesitter.txt b/doc/nvim-treesitter.txt
index f1c278427..c9adf93b2 100644
--- a/doc/nvim-treesitter.txt
+++ b/doc/nvim-treesitter.txt
@@ -52,9 +52,10 @@ By default, everything is disabled. To enable support for features, in your `ini
==============================================================================
COMMANDS *nvim-treesitter-commands*
-|:TSInstall| {language} *:TSInstall*
+|:TSInstall| {language} ... *:TSInstall*
-Download, compile and install a parser for {language}
+Install one or more treesitter parsers.
+You can use |:TSInstall| `all` to install all parsers.
|:TSInstallInfo| *:TSInstallInfo*
@@ -91,26 +92,29 @@ A list of languages can be found at |:TSInstallInfo|
List modules state for the current session.
==============================================================================
-API *nvim-treesitter-api*
+UTILS *nvim-treesitter-utils*
-Nvim treesitter exposes extended functions to use on nodes and scopes.
-you can retrieve the api with:
+Nvim treesitter has some wrapper functions that you can retrieve with:
>
- local ts_node_api = require 'nvim-treesitter'.get_node_api()
+ local ts_utils = require 'nvim-treesitter.ts_utils'
<
Methods
-get_node_text(node, bufnr) *ts_api.get_node_text*
+get_node_at_cursor(winnr) *ts_utils.get_node_at_cursor*
+ winnr will be 0 if nil
+ returns the node under the cursor
+
+get_node_text(node, bufnr) *ts_utils.get_node_text*
return the text content of a node
-is_parent(dest, source) *ts_api.is_parent*
+is_parent(dest, source) *ts_utils.is_parent*
determines wether `dest` is a parent of `source`
return a boolean
-get_named_children(node) *ts_api.get_named_children*
+get_named_children(node) *ts_utils.get_named_children*
return a table of named children of `node`
-get_next_node(node, allow_switch_parent, allow_next_parent) *ts_api.get_next_node*
+get_next_node(node, allow_switch_parent, allow_next_parent) *ts_utils.get_next_node*
return the next node within the same parent.
if no node is found, returns `nil`.
if `allow_switch_parent` is true, it will allow switching parent
@@ -118,42 +122,28 @@ get_next_node(node, allow_switch_parent, allow_next_parent) *ts_api.get_next_nod
if `allow_next_parent` is true, it will allow next parent if
the node is the last node and the next parent doesn't have children.
-get_previous_node(node, allow_switch_parents, allow_prev_parent) *ts_api.get_previous_node*
+get_previous_node(node, allow_switch_parents, allow_prev_parent) *ts_utils.get_previous_node*
return the previous node within the same parent.
`allow_switch_parent` and `allow_prev_parent` follow the same rule
- as |ts_api.get_next_node| but if the node is the first node.
+ as |ts_utils.get_next_node| but if the node is the first node.
-containing_scope(node) *ts_api.containing_scope*
+containing_scope(node) *ts_utils.containing_scope*
return the smallest scope containing the node
-parent_scope(node, cursor_pos) *ts_api.parent_scope*
+parent_scope(node, cursor_pos) *ts_utils.parent_scope*
return the parent scope of the current scope that contains the node.
`cursor_pos` should be `{ row = number, col = number }`
- you can retrieve the cursor_pos with the buffer state
-nested_scope(node, cursor_pos) *ts_api.nested_scope*
+nested_scope(node, cursor_pos) *ts_utils.nested_scope*
return the first scope within current scope that contains the node.
`cursor_pos` should be `{ row = number, col = number }`
- you can retrieve the cursor_pos with the buffer state
-next_scope(node) *ts_api.next_scope*
+next_scope(node) *ts_utils.next_scope*
return the neighbour scope of the current node
-previous_scope(node) *ts_api.previous_scope*
+previous_scope(node) *ts_utils.previous_scope*
return the previous neighbour scope of the current node
-Nvim-treesitter also provides access to the state of the current buffer:
->
- local cur_buf_state = require'nvim-treesitter'.get_buf_state()
- print(vim.inspect(cur_buf_state))
- --[[
- {
- cursor_pos = { row = number, col = number }, (current cursor pos in the buffer)
- current_node = tsnode (smallest node the cursor is on)
- }
- ]]--
-<
-
==============================================================================
FUNCTIONS *nvim-treesitter-functions*
@@ -177,4 +167,141 @@ Note: This is highly experimental, and folding can break on some types of
edits. If you encounter such breakage, hiting `zx` should fix folding.
In any case, feel free to open an issue with the reproducing steps.
+==============================================================================
+HIGHLIGHTS *nvim-treesitter-highlights*
+
+`TSError`
+ *hl-TSError*
+For syntax/parser errors.
+
+You can deactivate highlighting of syntax errors by adding this to your
+init.vim: >
+ highlight link TSError Normal
+
+`TSPunctDelimiter`
+ *hl-TSPunctDelimiter*
+For delimiters ie: `.`
+
+`TSPunctBracket`
+ *hl-TSPunctBracket*
+For brackets and parens.
+
+`TSPunctSpecial`
+ *hl-TSPunctSpecial*
+For special punctutation that does not fall in the catagories before.
+
+`TSConstant`
+ *hl-TSConstant*
+For constants
+
+`TSConstBuiltin`
+ *hl-TSConstBuiltin*
+For constant that are built in the language: `nil` in Lua.
+
+`TSConstMacro`
+ *hl-TSConstMacro*
+For constants that are defined by macros: `NULL` in C.
+
+`TSString`
+ *hl-TSString*
+For strings.
+
+`TSStringRegex`
+ *hl-TSStringRegex*
+For regexes.
+
+`TSStringEscape`
+ *hl-TSStringEscape*
+For escape characters within a string.
+
+`TSCharacter`
+ *hl-TSCharacter*
+For characters.
+
+`TSNumber`
+ *hl-TSNumber*
+For integers.
+
+`TSBoolean`
+ *hl-TSBoolean*
+For booleans.
+
+`TSFloat`
+ *hl-TSFloat*
+For floats.
+
+`TSFunction`
+ *hl-TSFunction*
+For function (calls and definitions).
+
+`TSFuncBuiltin`
+ *hl-TSFuncBuiltin*
+For builtin functions: `table.insert` in Lua.
+
+`TSFuncMacro`
+ *hl-TSFuncMacro*
+For macro defined fuctions (calls and definitions): each `macro_rules` in
+Rust.
+
+`TSParameter`
+ *hl-TSParameter*
+For parameters of a function.
+
+`TSMethod`
+ *hl-TSMethod*
+For method calls and definitions.
+
+`TSField`
+ *hl-TSField*
+For fields.
+
+`TSProperty`
+ *hl-TSProperty*
+Same as `TSField`.
+
+`TSConstructor`
+ *hl-TSConstructor*
+For constructor calls and definitions: `{}` in Lua, and Java constructors.
+
+`TSConditional`
+ *hl-TSConditional*
+For keywords related to conditionnals.
+
+`TSRepeat`
+ *hl-TSRepeat*
+For keywords related to loops.
+
+`TSLabel`
+ *hl-TSLabel*
+For labels: `label:` in C and `:label:` in Lua.
+
+`TSOperator`
+ *hl-TSOperator*
+For any operator: `+`, but also `->` and `*` in C.
+
+`TSKeyword`
+ *hl-TSKeyword*
+For keywords that don't fall in previous categories.
+
+`TSException`
+ *hl-TSException*
+For exception related keywords.
+
+`TSType`
+ *hl-TSType*
+For types.
+
+`TSTypeBuiltin`
+ *hl-TSTypeBuiltin*
+For builtin types (you guessed it, right ?).
+
+`TSStructure`
+ *hl-TSStructure*
+This is left as an exercise for the reader.
+
+`TSInclude`
+ *hl-TSInclude*
+For includes: `#include` in C, `use` or `extern crate` in Rust, or `require`
+in Lua
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/doc/tags b/doc/tags
deleted file mode 100644
index 0a12b18d5..000000000
--- a/doc/tags
+++ /dev/null
@@ -1,25 +0,0 @@
-:TSBufDisable nvim-treesitter.txt /*:TSBufDisable*
-:TSBufDisableAll nvim-treesitter.txt /*:TSBufDisableAll*
-:TSBufEnable nvim-treesitter.txt /*:TSBufEnable*
-:TSBufEnableAll nvim-treesitter.txt /*:TSBufEnableAll*
-:TSInstall nvim-treesitter.txt /*:TSInstall*
-:TSInstallInfo nvim-treesitter.txt /*:TSInstallInfo*
-:TSModuleInfo nvim-treesitter.txt /*:TSModuleInfo*
-nvim-treesitter nvim-treesitter.txt /*nvim-treesitter*
-nvim-treesitter-api nvim-treesitter.txt /*nvim-treesitter-api*
-nvim-treesitter-commands nvim-treesitter.txt /*nvim-treesitter-commands*
-nvim-treesitter-functions nvim-treesitter.txt /*nvim-treesitter-functions*
-nvim-treesitter-intro nvim-treesitter.txt /*nvim-treesitter-intro*
-nvim-treesitter-quickstart nvim-treesitter.txt /*nvim-treesitter-quickstart*
-nvim_treesitter#foldexpr() nvim-treesitter.txt /*nvim_treesitter#foldexpr()*
-nvim_treesitter#statusline() nvim-treesitter.txt /*nvim_treesitter#statusline()*
-ts_api.containing_scope nvim-treesitter.txt /*ts_api.containing_scope*
-ts_api.get_named_children nvim-treesitter.txt /*ts_api.get_named_children*
-ts_api.get_next_node nvim-treesitter.txt /*ts_api.get_next_node*
-ts_api.get_node_text nvim-treesitter.txt /*ts_api.get_node_text*
-ts_api.get_previous_node nvim-treesitter.txt /*ts_api.get_previous_node*
-ts_api.is_parent nvim-treesitter.txt /*ts_api.is_parent*
-ts_api.nested_scope nvim-treesitter.txt /*ts_api.nested_scope*
-ts_api.next_scope nvim-treesitter.txt /*ts_api.next_scope*
-ts_api.parent_scope nvim-treesitter.txt /*ts_api.parent_scope*
-ts_api.previous_scope nvim-treesitter.txt /*ts_api.previous_scope*
diff --git a/lua/nvim-treesitter.lua b/lua/nvim-treesitter.lua
index 100324e36..553ec3f65 100644
--- a/lua/nvim-treesitter.lua
+++ b/lua/nvim-treesitter.lua
@@ -2,10 +2,10 @@ local api = vim.api
local install = require'nvim-treesitter.install'
local utils = require'nvim-treesitter.utils'
+local ts_utils = require'nvim-treesitter.ts_utils'
local info = require'nvim-treesitter.info'
local configs = require'nvim-treesitter.configs'
-local state = require'nvim-treesitter.state'
-local ts_utils = require'nvim-treesitter.ts_utils'
+local parsers = require'nvim-treesitter.parsers'
local M = {}
@@ -14,27 +14,23 @@ function M.setup()
utils.setup_commands('info', info.commands)
utils.setup_commands('configs', configs.commands)
- for _, ft in pairs(configs.available_parsers()) do
+ for _, lang in pairs(parsers.available_parsers()) do
for _, mod in pairs(configs.available_modules()) do
- if configs.is_enabled(mod, ft) then
+ if configs.is_enabled(mod, lang) then
local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
- api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
+ for _, ft in pairs(parsers.lang_to_ft(lang)) do
+ api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
+ end
end
end
- local cmd = string.format("lua require'nvim-treesitter.state'.attach_to_buffer(%s)", ft)
- api.nvim_command(string.format('autocmd NvimTreesitter FileType %s %s', ft, cmd))
end
-
- state.run_update()
end
function M.statusline(indicator_size)
+ if not parsers.has_parser() then return end
local indicator_size = indicator_size or 100
- local bufnr = api.nvim_get_current_buf()
- local buf_state = state.get_buf_state(bufnr)
- if not buf_state then return "" end
- local current_node = buf_state.current_node
+ local current_node = ts_utils.get_node_at_cursor()
if not current_node then return "" end
local expr = current_node:parent()
@@ -56,13 +52,4 @@ function M.statusline(indicator_size)
end
end
-function M.get_buf_state()
- local bufnr = api.nvim_get_current_buf()
- return state.exposed_state(bufnr)
-end
-
-function M.get_node_api()
- return ts_utils
-end
-
return M
diff --git a/lua/nvim-treesitter/configs.lua b/lua/nvim-treesitter/configs.lua
index f7b5ff2cf..43dc944bf 100644
--- a/lua/nvim-treesitter/configs.lua
+++ b/lua/nvim-treesitter/configs.lua
@@ -1,215 +1,9 @@
local api = vim.api
local queries = require'nvim-treesitter.query'
+local parsers = require'nvim-treesitter.parsers'
local utils = require'nvim-treesitter.utils'
-local parsers = {}
-
-parsers.javascript = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-javascript",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.c = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-c",
- files = { "src/parser.c" }
- }
-}
-
-parsers.cpp = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-cpp",
- files = { "src/parser.c", "src/scanner.cc" }
- }
-}
-
-parsers.rust = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-rust",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.lua = {
- install_info = {
- url = "https://github.com/nvim-treesitter/tree-sitter-lua",
- files = { "src/parser.c", "src/scanner.cc" }
- }
-}
-
-parsers.python = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-python",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.go = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-go",
- files = { "src/parser.c" },
- }
-}
-
-parsers.ruby = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-ruby",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.bash = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-bash",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.php = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-php",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.java = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-java",
- files = { "src/parser.c" },
- }
-}
-
-parsers.html = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-html",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.julia = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-julia",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.json = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-json",
- files = { "src/parser.c" },
- }
-}
-
-parsers.css = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-css",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.ocaml = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-ocaml",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.swift = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-swift",
- files = { "src/parser.c" },
- }
-}
-
-parsers.csharp = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-c-sharp",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.typescript = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-typescript",
- files = { "src/parser.c", "src/scanner.c" },
- location = "tree-sitter-typescript/typescript"
- }
-}
-
-parsers.tsx = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-typescript",
- files = { "src/parser.c", "src/scanner.c" },
- location = "tree-sitter-tsx/tsx"
- }
-}
-
-parsers.scala = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-scala",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.haskell = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-haskell",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.markdown = {
- install_info = {
- url = "https://github.com/ikatyang/tree-sitter-markdown",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.toml = {
- install_info = {
- url = "https://github.com/ikatyang/tree-sitter-toml",
- files = { "src/parser.c", "src/scanner.c" },
- }
-}
-
-parsers.vue = {
- install_info = {
- url = "https://github.com/ikatyang/tree-sitter-vue",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.elm = {
- install_info = {
- url = "https://github.com//razzeee/tree-sitter-elm",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.yaml = {
- install_info = {
- url = "https://github.com/ikatyang/tree-sitter-yaml",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.nix = {
- install_info = {
- url = "https://github.com/cstrahan/tree-sitter-nix",
- files = { "src/parser.c", "src/scanner.cc" },
- }
-}
-
-parsers.regex = {
- install_info = {
- url = "https://github.com/tree-sitter/tree-sitter-regex",
- files = { "src/parser.c" }
- }
-}
-
-- @enable can be true or false
-- @disable is a list of languages, only relevant if enable is true
-- @keymaps list of user mappings for a given module if relevant
@@ -219,9 +13,7 @@ local config = {
highlight = {
enable = false,
disable = {},
- is_supported = function(ft)
- return queries.get_query(ft, 'highlights') ~= nil
- end
+ is_supported = queries.has_highlights
},
incremental_selection = {
enable = false,
@@ -232,9 +24,31 @@ local config = {
scope_incremental="grc",
node_decremental="grm"
},
- is_supported = function(ft)
- return queries.get_query(ft, 'locals')
- end
+ is_supported = queries.has_locals
+ },
+ refactor = {
+ highlight_definitions = {
+ enable = false,
+ disable = {},
+ is_supported = queries.has_locals
+ },
+ smart_rename = {
+ enable = false,
+ disable = {},
+ is_supported = queries.has_locals,
+ keymaps = {
+ smart_rename = "grr"
+ }
+ },
+ navigation = {
+ enable = false,
+ disable = {},
+ is_supported = queries.has_locals,
+ keymaps = {
+ goto_definition = "gnd",
+ list_definitions = "gnD"
+ }
+ }
}
},
ensure_installed = nil
@@ -242,83 +56,125 @@ local config = {
local M = {}
-local function enable_module(mod, bufnr, ft)
+local function enable_module(mod, bufnr, lang)
local bufnr = bufnr or api.nvim_get_current_buf()
- local ft = ft or api.nvim_buf_get_option(bufnr, 'ft')
- if not parsers[ft] or not config.modules[mod] then
+ local lang = lang or parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, 'ft'))
+
+ if not parsers.list[lang] or not M.get_module(mod) then
return
end
local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
- loaded_mod.attach(bufnr, ft)
+ loaded_mod.attach(bufnr, lang)
end
-local function enable_mod_conf_autocmd(mod, ft)
- if not config.modules[mod] or M.is_enabled(mod, ft) then return end
+local function enable_mod_conf_autocmd(mod, lang)
+ local config_mod = M.get_module(mod)
+
+ if not config_mod or M.is_enabled(mod, lang) then return end
local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
- api.nvim_command(string.format("autocmd FileType %s %s", ft, cmd))
- for i, parser in pairs(config.modules[mod].disable) do
- if parser == ft then
- table.remove(config.modules[mod].disable, i)
+ for _, ft in pairs(parsers.lang_to_ft(lang)) do
+ api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
+ end
+ for i, parser in pairs(config_mod.disable) do
+ if parser == lang then
+ table.remove(config_mod.disable, i)
break
end
end
end
-local function enable_all(mod, ft)
- if not config.modules[mod] then return end
+local function enable_all(mod, lang)
+ local config_mod = M.get_module(mod)
+
+ if not config_mod then return end
for _, bufnr in pairs(api.nvim_list_bufs()) do
- if not ft or api.nvim_buf_get_option(bufnr, 'ft') == ft then
- enable_module(mod, bufnr, ft)
+ local ft = api.nvim_buf_get_option(bufnr, 'ft')
+ if not lang or parsers.lang_match_ft(lang, ft) then
+ enable_module(mod, bufnr, lang)
end
end
- if ft then
- if utils.has_parser(ft) then
- enable_mod_conf_autocmd(mod, ft)
+ if lang then
+ if parsers.has_parser(lang) then
+ enable_mod_conf_autocmd(mod, lang)
end
else
- for _, ft in pairs(M.available_parsers()) do
- if utils.has_parser(ft) then
- enable_mod_conf_autocmd(mod, ft)
+ for _, lang in pairs(parsers.available_parsers()) do
+ if parsers.has_parser(lang) then
+ enable_mod_conf_autocmd(mod, lang)
end
end
end
- config.modules[mod].enable = true
+ config_mod.enable = true
end
-local function disable_module(mod, bufnr, ft)
+local function disable_module(mod, bufnr, lang)
local bufnr = bufnr or api.nvim_get_current_buf()
- local ft = ft or api.nvim_buf_get_option(bufnr, 'ft')
- if not parsers[ft] or not config.modules[mod] then
+ local lang = lang or parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, 'ft'))
+ if not lang then
+ return
+ end
+
+ if not parsers.list[lang] or not M.get_module(mod) then
return
end
local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
- loaded_mod.detach(bufnr, ft)
+ loaded_mod.detach(bufnr)
end
-local function disable_mod_conf_autocmd(mod, ft)
- if not config.modules[mod] or not M.is_enabled(mod, ft) then return end
+local function disable_mod_conf_autocmd(mod, lang)
+ local config_mod = M.get_module(mod)
- api.nvim_command(string.format("autocmd! FileType %s", ft))
- table.insert(config.modules[mod].disable, ft)
+ if not config_mod or not M.is_enabled(mod, lang) then return end
+
+ --local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
+ -- TODO(kyazdani): detach the correct autocmd... doesn't work when using %s, cmd
+ for _, ft in pairs(parsers.lang_to_ft(lang)) do
+ api.nvim_command(string.format("autocmd! NvimTreesitter FileType %s", ft))
+ end
+ table.insert(config_mod.disable, lang)
end
-local function disable_all(mod, ft)
+local function disable_all(mod, lang)
for _, bufnr in pairs(api.nvim_list_bufs()) do
- if not ft or api.nvim_buf_get_option(bufnr, 'ft') == ft then
- disable_module(mod, bufnr, ft)
+ local ft = api.nvim_buf_get_option(bufnr, 'ft')
+ if not lang or parsers.lang_match_ft(lang, ft) then
+ disable_module(mod, bufnr, lang)
end
end
- if ft then
- disable_mod_conf_autocmd(mod, ft)
+ if lang then
+ disable_mod_conf_autocmd(mod, lang)
else
- for _, ft in pairs(M.available_parsers()) do
- disable_mod_conf_autocmd(mod, ft)
+ for _, lang in pairs(parsers.available_parsers()) do
+ disable_mod_conf_autocmd(mod, lang)
+ end
+
+ local config_mod = M.get_module(mod)
+
+ if config_mod then
+ config_mod.enable = false
+ end
+ end
+end
+
+-- Recurses trough all modules including submodules
+-- @param accumulator function called for each module
+-- @param root root configuration table to start at
+-- @param path prefix path
+local function recurse_modules(accumulator, root, path)
+ local root = root or config.modules
+
+ for name, module in pairs(root) do
+ local new_path = path and (path..'.'..name) or name
+
+ if M.is_module(module) then
+ accumulator(name, module, new_path)
+ elseif type(module) == 'table' then
+ recurse_modules(accumulator, module, new_path)
end
- config.modules[mod].enable = false
end
end
@@ -328,51 +184,47 @@ M.commands = {
args = {
"-nargs=1",
"-complete=custom,v:lua.ts_available_modules"
- },
- description = '`:TSBufEnable module_name` enable a specified module on the current buffer'
+ }
},
TSBufDisable = {
run = disable_module,
args = {
"-nargs=1",
"-complete=custom,v:lua.ts_available_modules"
- },
- description = '`:TSBufDisable module_name` disable a specified module on the current buffer'
+ }
},
TSEnableAll = {
run = enable_all,
args = {
"-nargs=+",
"-complete=custom,v:lua.ts_available_modules"
- },
- description = '`:TSEnableAll module_name (filetype)` enables a specified module on all buffers. If filetype is specified, enable only for specified filetype'
+ }
},
TSDisableAll = {
run = disable_all,
args = {
"-nargs=+",
"-complete=custom,v:lua.ts_available_modules"
- },
- description = '`:TSDisableAll module_name (filetype)` disables a specified module on all buffers. If filetype is specified, disable only for specified filetype'
+ }
},
}
-- @param mod: module (string)
-- @param ft: filetype (string)
-function M.is_enabled(mod, ft)
- if not M.get_parser_configs()[ft] or not utils.has_parser(ft) then
+function M.is_enabled(mod, lang)
+ if not parsers.list[lang] or not parsers.has_parser(lang) then
return false
end
- local module_config = config.modules[mod]
+ local module_config = M.get_module(mod)
if not module_config then return false end
- if not module_config.enable or not module_config.is_supported(ft) then
+ if not module_config.enable or not module_config.is_supported(lang) then
return false
end
for _, parser in pairs(module_config.disable) do
- if ft == parser then return false end
+ if lang == parser then return false end
end
return true
@@ -381,42 +233,63 @@ end
function M.setup(user_data)
if not user_data then return end
- for mod, data in pairs(user_data) do
- if config.modules[mod] then
- if type(data.enable) == 'boolean' then
- config.modules[mod].enable = data.enable
- end
- if type(data.disable) == 'table' then
- config.modules[mod].disable = data.disable
- end
- if config.modules[mod].keymaps and type(data.keymaps) == 'table' then
- for f, map in pairs(data.keymaps) do
- if config.modules[mod].keymaps[f] then
- config.modules[mod].keymaps[f] = map
- end
+ M.setup_module(config.modules, user_data)
+end
+
+-- Sets up a single module or all submodules of a group.
+-- Note, this method is recursive.
+-- @param mod the module or group of modules
+-- @param data user defined configuration for the module
+-- @param mod_name name of the module if it exists
+function M.setup_module(mod, data, mod_name)
+ if mod_name == 'ensure_installed' then
+ config.ensure_installed = data
+ require'nvim-treesitter.install'.ensure_installed(data)
+ elseif M.is_module(mod) then
+ if type(data.enable) == 'boolean' then
+ mod.enable = data.enable
+ end
+ if type(data.disable) == 'table' then
+ mod.disable = data.disable
+ end
+ if mod.keymaps and type(data.keymaps) == 'table' then
+ for f, map in pairs(data.keymaps) do
+ if mod.keymaps[f] then
+ mod.keymaps[f] = map
end
end
- elseif mod == 'ensure_installed' then
- config.ensure_installed = data
- require'nvim-treesitter.install'.ensure_installed(data)
+ end
+ elseif type(data) == 'table' and type(mod) == 'table' then
+ for key, value in pairs(data) do
+ M.setup_module(mod[key], value, key)
end
end
end
-function M.get_parser_configs()
- return parsers
-end
-
-function M.available_parsers()
- return vim.tbl_keys(parsers)
-end
-
function M.available_modules()
- return vim.tbl_keys(config.modules)
+ local modules = {}
+
+ recurse_modules(function(_, _, path)
+ table.insert(modules, path)
+ end)
+
+ return modules
end
-function M.get_module(mod)
- return config.modules[mod]
+-- Gets a module config by path
+-- @param mod_path path to the module
+-- @returns the module or nil
+function M.get_module(mod_path)
+ local mod = utils.get_at_path(config.modules, mod_path)
+
+ return M.is_module(mod) and mod or nil
+end
+
+-- Determines whether the provided table is a module.
+-- A module should contain an 'is_supported' function.
+-- @param mod the module table
+function M.is_module(mod)
+ return type(mod) == 'table' and type(mod.is_supported) == 'function'
end
return M
diff --git a/lua/nvim-treesitter/fold.lua b/lua/nvim-treesitter/fold.lua
index 6a39461a7..401f52606 100644
--- a/lua/nvim-treesitter/fold.lua
+++ b/lua/nvim-treesitter/fold.lua
@@ -1,4 +1,3 @@
-local api = vim.api
local parsers = require'nvim-treesitter.parsers'
local M = {}
@@ -21,7 +20,7 @@ function M.get_fold_indic(lnum)
local parser = parsers.get_parser()
- local multiline_here, level = smallest_multiline_containing(parser:parse():root(), 0)
+ local _, level = smallest_multiline_containing(parser:parse():root(), 0)
return tostring(level)
end
diff --git a/lua/nvim-treesitter/health.lua b/lua/nvim-treesitter/health.lua
index ca1e765c7..d3083e77b 100644
--- a/lua/nvim-treesitter/health.lua
+++ b/lua/nvim-treesitter/health.lua
@@ -2,13 +2,10 @@ local api = vim.api
local fn = vim.fn
local queries = require'nvim-treesitter.query'
-local locals = require'nvim-treesitter.locals'
-local highlight = require'nvim-treesitter.highlight'
-local configs = require'nvim-treesitter.configs'
+local parsers = require'nvim-treesitter.parsers'
local health_start = vim.fn["health#report_start"]
local health_ok = vim.fn['health#report_ok']
-local health_info = vim.fn['health#report_info']
local health_warn = vim.fn['health#report_warn']
local health_error = vim.fn['health#report_error']
@@ -62,7 +59,7 @@ function M.checkhealth()
local missing_parsers = {}
-- Parser installation checks
- for _, parser_name in pairs(configs.available_parsers()) do
+ for _, parser_name in pairs(parsers.available_parsers()) do
local installed = #api.nvim_get_runtime_file('parser/'..parser_name..'.so', false)
-- Only print informations about installed parsers
diff --git a/lua/nvim-treesitter/highlight.lua b/lua/nvim-treesitter/highlight.lua
index 1358790b1..1b4722435 100644
--- a/lua/nvim-treesitter/highlight.lua
+++ b/lua/nvim-treesitter/highlight.lua
@@ -2,6 +2,7 @@ local api = vim.api
local ts = vim.treesitter
local queries = require'nvim-treesitter.query'
+local parsers = require'nvim-treesitter.parsers'
local M = {
highlighters = {}
@@ -10,53 +11,54 @@ local M = {
local hlmap = vim.treesitter.TSHighlighter.hl_map
-- Misc
-hlmap.error = "Error"
-hlmap["punctuation.delimiter"] = "Delimiter"
-hlmap["punctuation.bracket"] = "Delimiter"
+hlmap.error = "TSError"
+hlmap["punctuation.delimiter"] = "TSPunctDelimiter"
+hlmap["punctuation.bracket"] = "TSPunctBracket"
+hlmap["punctuation.special"] = "TSPunctSpecial"
-- Constants
-hlmap["constant"] = "Constant"
-hlmap["constant.builtin"] = "Special"
-hlmap["constant.macro"] = "Define"
-hlmap["string"] = "String"
-hlmap["string.regex"] = "String"
-hlmap["string.escape"] = "SpecialChar"
-hlmap["character"] = "Character"
-hlmap["number"] = "Number"
-hlmap["boolean"] = "Boolean"
-hlmap["float"] = "Float"
+hlmap["constant"] = "TSConstant"
+hlmap["constant.builtin"] = "TSConstBuiltin"
+hlmap["constant.macro"] = "TSConstMacro"
+hlmap["string"] = "TSString"
+hlmap["string.regex"] = "TSStringRegex"
+hlmap["string.escape"] = "TSStringEscape"
+hlmap["character"] = "TSCharacter"
+hlmap["number"] = "TSNumber"
+hlmap["boolean"] = "TSBoolean"
+hlmap["float"] = "TSFloat"
-- Functions
-hlmap["function"] = "Function"
-hlmap["function.builtin"] = "Special"
-hlmap["function.macro"] = "Macro"
-hlmap["parameter"] = "Identifier"
-hlmap["method"] = "Function"
-hlmap["field"] = "Identifier"
-hlmap["property"] = "Identifier"
-hlmap["constructor"] = "Constant"
+hlmap["function"] = "TSFunction"
+hlmap["function.builtin"] = "TSFuncBuiltin"
+hlmap["function.macro"] = "TSFuncMacro"
+hlmap["parameter"] = "TSIdentifier"
+hlmap["method"] = "TSMethod"
+hlmap["field"] = "TSField"
+hlmap["property"] = "TSProperty"
+hlmap["constructor"] = "TSConstructor"
-- Keywords
-hlmap["conditional"] = "Conditional"
-hlmap["repeat"] = "Repeat"
-hlmap["label"] = "Label"
-hlmap["operator"] = "Operator"
-hlmap["keyword"] = "Keyword"
-hlmap["exception"] = "Exception"
+hlmap["conditional"] = "TSConditional"
+hlmap["repeat"] = "TSRepeat"
+hlmap["label"] = "TSLabel"
+hlmap["operator"] = "TSOperator"
+hlmap["keyword"] = "TSKeyword"
+hlmap["exception"] = "TSException"
-hlmap["type"] = "Type"
-hlmap["type.builtin"] = "Type"
-hlmap["structure"] = "Structure"
-hlmap["include"] = "Include"
+hlmap["type"] = "TSType"
+hlmap["type.builtin"] = "TSTypeBuiltin"
+hlmap["structure"] = "TSStructure"
+hlmap["include"] = "TSInclude"
-function M.attach(bufnr, ft)
- local buf = bufnr or api.nvim_get_current_buf()
- local ft = ft or api.nvim_buf_get_option(buf, 'ft')
+function M.attach(bufnr, lang)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local lang = lang or parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, 'ft'))
- local query = queries.get_query(ft, "highlights")
+ local query = queries.get_query(lang, "highlights")
if not query then return end
- M.highlighters[buf] = ts.TSHighlighter.new(query, buf, ft)
+ M.highlighters[bufnr] = ts.TSHighlighter.new(query, bufnr, lang)
end
function M.detach(bufnr)
diff --git a/lua/nvim-treesitter/incremental_selection.lua b/lua/nvim-treesitter/incremental_selection.lua
index 9876f2e01..3b76b25f2 100644
--- a/lua/nvim-treesitter/incremental_selection.lua
+++ b/lua/nvim-treesitter/incremental_selection.lua
@@ -1,11 +1,13 @@
local api = vim.api
-local state = require'nvim-treesitter.state'
local configs = require'nvim-treesitter.configs'
local ts_utils = require'nvim-treesitter.ts_utils'
+local parsers = require'nvim-treesitter.parsers'
local M = {}
+local selections = {}
+
local function update_selection(buf, node)
local start_row, start_col, end_row, end_col = node:range()
@@ -18,32 +20,49 @@ local function update_selection(buf, node)
vim.fn.setpos(".", { buf, end_row+1, end_col+1, 0 })
end
+function M.init_selection()
+ local buf = api.nvim_get_current_buf()
+ local node = ts_utils.get_node_at_cursor()
+ selections[buf] = { [1] = node }
+ update_selection(buf, node)
+end
+
+local function visual_selection_range()
+ local _, csrow, cscol, _ = unpack(vim.fn.getpos("'<"))
+ local _, cerow, cecol, _ = unpack(vim.fn.getpos("'>"))
+ if csrow < cerow then
+ return csrow-1, cscol-1, cerow-1, cecol-1
+ else
+ return cerow-1, cecol-1, csrow-1, cscol-1
+ end
+end
+
+local function range_matches(node)
+ local csrow, cscol, cerow, cecol = visual_selection_range()
+ local srow, scol, erow, ecol = node:range()
+ return srow == csrow and scol == cscol and erow == cerow and ecol == cecol
+end
+
local function select_incremental(get_parent)
return function()
local buf = api.nvim_get_current_buf()
- local buf_state = state.get_buf_state(buf)
+ local nodes = selections[buf]
- local node
- -- initialize incremental selection with current range
- if #buf_state.selection.nodes == 0 then
- local cur_range = buf_state.selection.range
- if not cur_range then
- local _, cursor_row, cursor_col, _ = unpack(vim.fn.getpos("."))
- cur_range = { cursor_row, cursor_col, cursor_row, cursor_col + 1 }
- end
-
- local root = buf_state.parser.tree:root()
- if not root then return end
-
- node = root:named_descendant_for_range(cur_range[1]-1, cur_range[2]-1, cur_range[3]-1, cur_range[4]-1)
- else
- node = get_parent(buf_state.selection.nodes[#buf_state.selection.nodes])
+ -- initialize incremental selection with current selection
+ if not nodes or #nodes == 0 or not range_matches(nodes[#nodes]) then
+ local csrow, cscol, cerow, cecol = visual_selection_range()
+ local root = parsers.get_parser().tree:root()
+ local node = root:named_descendant_for_range(csrow, cscol, cerow, cecol)
+ update_selection(buf, node)
+ selections[buf] = { [1] = node }
+ return
end
+ local node = get_parent(nodes[#nodes])
if not node then return end
- if node ~= buf_state.selection.nodes[#buf_state.selection.nodes] then
- state.insert_selection_node(buf, node)
+ if node ~= nodes[#nodes] then
+ table.insert(nodes, node)
end
update_selection(buf, node)
@@ -60,13 +79,10 @@ end)
function M.node_decremental()
local buf = api.nvim_get_current_buf()
- local buf_state = state.get_buf_state(buf)
-
- local nodes = buf_state.selection.nodes
- if #nodes < 2 then return end
-
- state.pop_selection_node(buf)
+ local nodes = selections[buf]
+ if not nodes or #nodes < 2 then return end
+ table.remove(selections[buf])
local node = nodes[#nodes]
update_selection(buf, node)
end
@@ -76,14 +92,14 @@ function M.attach(bufnr)
local config = configs.get_module('incremental_selection')
for funcname, mapping in pairs(config.keymaps) do
-
+ local mode
if funcname == "init_selection" then
- local cmd = ":lua require'nvim-treesitter.incremental_selection'.node_incremental()"
- api.nvim_buf_set_keymap(buf, 'n', mapping, cmd, { silent = true })
+ mode = 'n'
else
- local cmd = string.format(":lua require'nvim-treesitter.incremental_selection'.%s()", funcname)
- api.nvim_buf_set_keymap(buf, 'v', mapping, cmd, { silent = true })
+ mode = 'v'
end
+ local cmd = string.format(":lua require'nvim-treesitter.incremental_selection'.%s()", funcname)
+ api.nvim_buf_set_keymap(buf, mode, mapping, cmd, { silent = true })
end
end
diff --git a/lua/nvim-treesitter/info.lua b/lua/nvim-treesitter/info.lua
index 18b1b611f..98b759518 100644
--- a/lua/nvim-treesitter/info.lua
+++ b/lua/nvim-treesitter/info.lua
@@ -1,15 +1,16 @@
local api = vim.api
local configs = require'nvim-treesitter.configs'
+local parsers = require'nvim-treesitter.parsers'
local M = {}
local function install_info()
local max_len = 0
- for _, ft in pairs(configs.available_parsers()) do
+ for _, ft in pairs(parsers.available_parsers()) do
if #ft > max_len then max_len = #ft end
end
- for _, ft in pairs(configs.available_parsers()) do
+ for _, ft in pairs(parsers.available_parsers()) do
local is_installed = #api.nvim_get_runtime_file('parser/'..ft..'.so', false) > 0
api.nvim_out_write(ft..string.rep(' ', max_len - #ft + 1))
if is_installed then
@@ -20,14 +21,14 @@ local function install_info()
end
end
-local function print_info_module(sorted_filetypes, mod)
- local max_str_len = #sorted_filetypes[1]
+local function print_info_module(sorted_languages, mod)
+ local max_str_len = #sorted_languages[1]
local header = string.format('%s%s', string.rep(' ', max_str_len + 2), mod)
api.nvim_out_write(header..'\n')
- for _, ft in pairs(sorted_filetypes) do
- local padding = string.rep(' ', max_str_len - #ft + #mod / 2 + 1)
- api.nvim_out_write(ft..":"..padding)
- if configs.is_enabled(mod, ft) then
+ for _, lang in pairs(sorted_languages) do
+ local padding = string.rep(' ', max_str_len - #lang + #mod / 2 + 1)
+ api.nvim_out_write(lang..":"..padding)
+ if configs.is_enabled(mod, lang) then
api.nvim_out_write('✓')
else
api.nvim_out_write('✗')
@@ -36,23 +37,23 @@ local function print_info_module(sorted_filetypes, mod)
end
end
-local function print_info_modules(sorted_filetypes)
- local max_str_len = #sorted_filetypes[1]
+local function print_info_modules(sorted_languages)
+ local max_str_len = #sorted_languages[1]
local header = string.rep(' ', max_str_len + 2)
for _, mod in pairs(configs.available_modules()) do
header = string.format('%s%s ', header, mod)
end
api.nvim_out_write(header..'\n')
- for _, ft in pairs(sorted_filetypes) do
- local padding = string.rep(' ', max_str_len - #ft)
- api.nvim_out_write(ft..":"..padding)
+ for _, lang in pairs(sorted_languages) do
+ local padding = string.rep(' ', max_str_len - #lang)
+ api.nvim_out_write(lang..":"..padding)
for _, mod in pairs(configs.available_modules()) do
local pad_len = #mod / 2 + 1
api.nvim_out_write(string.rep(' ', pad_len))
- if configs.is_enabled(mod, ft) then
+ if configs.is_enabled(mod, lang) then
api.nvim_out_write('✓')
else
api.nvim_out_write('✗')
@@ -64,14 +65,14 @@ local function print_info_modules(sorted_filetypes)
end
local function module_info(mod)
- if mod and not configs.get_config()[mod] then return end
+ if mod and not configs.get_module(mod) then return end
- local ft_by_len = configs.available_parsers()
- table.sort(ft_by_len, function(a, b) return #a > #b end)
+ local parserlist = parsers.available_parsers()
+ table.sort(parserlist, function(a, b) return #a > #b end)
if mod then
- print_info_module(ft_by_len, mod)
+ print_info_module(parserlist, mod)
else
- print_info_modules(ft_by_len)
+ print_info_modules(parserlist)
end
end
@@ -80,16 +81,14 @@ M.commands = {
run = install_info,
args = {
"-nargs=0",
- },
- description = '`:TSInstallInfo` print installation state for every filetype'
+ }
},
TSModuleInfo = {
run = module_info,
args = {
"-nargs=?",
"-complete=custom,v:lua.ts_available_modules"
- },
- description = '`:TSModuleInfo` print module state for every filetype, if module is specified, only for current module'
+ }
}
}
diff --git a/lua/nvim-treesitter/install.lua b/lua/nvim-treesitter/install.lua
index 637f1dedb..f2972005c 100644
--- a/lua/nvim-treesitter/install.lua
+++ b/lua/nvim-treesitter/install.lua
@@ -2,13 +2,13 @@ local api = vim.api
local fn = vim.fn
local luv = vim.loop
-local configs = require'nvim-treesitter.configs'
local utils = require'nvim-treesitter.utils'
+local parsers = require'nvim-treesitter.parsers'
local M = {}
-local function iter_cmd(cmd_list, i, ft)
- if i == #cmd_list + 1 then return print('Treesitter parser for '..ft..' has been installed') end
+local function iter_cmd(cmd_list, i, lang)
+ if i == #cmd_list + 1 then return print('Treesitter parser for '..lang..' has been installed') end
local attr = cmd_list[i]
if attr.info then print(attr.info) end
@@ -18,22 +18,57 @@ local function iter_cmd(cmd_list, i, ft)
handle = luv.spawn(attr.cmd, attr.opts, vim.schedule_wrap(function(code)
handle:close()
if code ~= 0 then return api.nvim_err_writeln(attr.err) end
- iter_cmd(cmd_list, i + 1, ft)
+ iter_cmd(cmd_list, i + 1, lang)
end))
end
-local function run_install(cache_folder, package_path, ft, repo)
- local project_name = 'tree-sitter-'..ft
+local function get_command(cmd)
+ local ret = ''
+ if cmd.opts and cmd.opts.cwd then
+ ret = string.format('cd %s;\n', cmd.opts.cwd)
+ end
+
+ ret = string.format('%s%s ', ret, cmd.cmd)
+
+ local options = ""
+ if cmd.opts and cmd.opts.args then
+ for _, opt in ipairs(cmd.opts.args) do
+ options = string.format("%s %s", options, opt)
+ end
+ end
+
+ return string.format('%s%s', ret, options)
+end
+
+local function iter_cmd_sync(cmd_list)
+ for _, cmd in ipairs(cmd_list) do
+ if cmd.info then
+ print(cmd.info)
+ end
+
+ vim.fn.system(get_command(cmd))
+ if vim.v.shell_error ~= 0 then
+ api.nvim_err_writeln(cmd.err)
+ return false
+ end
+
+ end
+
+ return true
+end
+
+local function run_install(cache_folder, package_path, lang, repo, with_sync)
+ local project_name = 'tree-sitter-'..lang
local project_repo = cache_folder..'/'..project_name
-- compile_location only needed for typescript installs.
local compile_location = cache_folder..'/'..(repo.location or project_name)
- local parser_lib_name = package_path.."/parser/"..ft..".so"
+ local parser_lib_name = package_path.."/parser/"..lang..".so"
local command_list = {
{
cmd = 'rm',
opts = {
args = { '-rf', project_repo },
- }
+ },
},
{
cmd = 'git',
@@ -76,73 +111,87 @@ local function run_install(cache_folder, package_path, ft, repo)
}
}
- iter_cmd(command_list, 1, ft)
+ if with_sync then
+ if iter_cmd_sync(command_list, lang) == true then
+ print('Treesitter parser for '..lang..' has been installed')
+ end
+ else
+ iter_cmd(command_list, 1, lang)
+ end
+end
+
+local function install_lang(lang, ask_reinstall, cache_folder, package_path, with_sync)
+ if #api.nvim_get_runtime_file('parser/'..lang..'.so', false) > 0 then
+ if not ask_reinstall then return end
+
+ local yesno = fn.input(lang .. ' parser already available: would you like to reinstall ? y/n: ')
+ print('\n ') -- mandatory to avoid messing up command line
+ if not string.match(yesno, '^y.*') then return end
+ end
+
+ local parser_config = parsers.get_parser_configs()[lang]
+ if not parser_config then
+ return api.nvim_err_writeln('Parser not available for language '..lang)
+ end
+
+ local install_info = parser_config.install_info
+ vim.validate {
+ url={ install_info.url, 'string' },
+ files={ install_info.files, 'table' }
+ }
+
+ run_install(cache_folder, package_path, lang, install_info, with_sync)
end
-- TODO(kyazdani): this should work on windows too
-local function install(...)
- if fn.has('win32') == 1 then
- return api.nvim_err_writeln('This command is not available on windows at the moment.')
- end
-
- if fn.executable('git') == 0 then
- return api.nvim_err_writeln('Git is required on your system to run this command')
- end
-
- local package_path, err = utils.get_package_path()
- if err then return api.nvim_err_writeln(err) end
-
- local cache_folder, err = utils.get_cache_dir()
- if err then return api.nvim_err_writeln(err) end
-
- for _, ft in ipairs({ ... }) do
- if #api.nvim_get_runtime_file('parser/'..ft..'.so', false) > 0 then
- local yesno = fn.input(ft .. ' parser already available: would you like to reinstall ? y/n: ')
- print('\n ') -- mandatory to avoid messing up command line
- if not string.match(yesno, '^y.*') then return end
+local function install(with_sync, ask_reinstall)
+ return function (...)
+ if fn.has('win32') == 1 then
+ return api.nvim_err_writeln('This command is not available on windows at the moment.')
end
- local parser_config = configs.get_parser_configs()[ft]
- if not parser_config then
- return api.nvim_err_writeln('Parser not available for language '..ft)
+ if fn.executable('git') == 0 then
+ return api.nvim_err_writeln('Git is required on your system to run this command')
end
- local install_info = parser_config.install_info
- vim.validate {
- url={ install_info.url, 'string' },
- files={ install_info.files, 'table' }
- }
+ local package_path, err = utils.get_package_path()
+ if err then return api.nvim_err_writeln(err) end
- run_install(cache_folder, package_path, ft, install_info)
- end
-end
+ local cache_folder, err = utils.get_cache_dir()
+ if err then return api.nvim_err_writeln(err) end
-
-M.ensure_installed = function(languages)
- if type(languages) == 'string' then
- if languages == 'all' then
- languages = configs.available_parsers()
+ local languages
+ local ask
+ if ... == 'all' then
+ languages = parsers.available_parsers()
+ ask = false
else
- languages = {languages}
+ languages = vim.tbl_flatten({...})
+ ask = ask_reinstall
end
- end
- for _, ft in ipairs(languages) do
- if not utils.has_parser(ft) then
- install(ft)
+ for _, lang in ipairs(languages) do
+ install_lang(lang, ask, cache_folder, package_path, with_sync)
end
end
end
+M.ensure_installed = install(false, false)
M.commands = {
TSInstall = {
- run = install,
+ run = install(false, true),
args = {
"-nargs=+",
"-complete=custom,v:lua.ts_installable_parsers"
- },
- description = '`:TSInstall {ft}` installs a parser under nvim-treesitter/parser/{name}.so'
+ }
+ },
+ TSInstallSync = {
+ run = install(true, true),
+ args = {
+ "-nargs=+",
+ "-complete=custom,v:lua.ts_installable_parsers"
+ }
}
}
diff --git a/lua/nvim-treesitter/locals.lua b/lua/nvim-treesitter/locals.lua
index 313d7655d..b52d42b3f 100644
--- a/lua/nvim-treesitter/locals.lua
+++ b/lua/nvim-treesitter/locals.lua
@@ -2,23 +2,36 @@
-- Locals are a generalization of definition and scopes
-- its the way nvim-treesitter uses to "understand" the code
local api = vim.api
-local ts = vim.treesitter
local queries = require'nvim-treesitter.query'
+local parsers = require'nvim-treesitter.parsers'
local utils = require'nvim-treesitter.utils'
-local M = {
- locals = {}
+local default_dict = {
+ __index = function(table, key)
+ local exists = rawget(table, key)
+ if not exists then
+ table[key] = {}
+ end
+ return rawget(table, key)
+ end
}
-function M.collect_locals(bufnr)
- local ft = api.nvim_buf_get_option(bufnr, "ft")
- if not ft then return end
+local query_cache = {}
+setmetatable(query_cache, default_dict)
- local query = queries.get_query(ft, 'locals')
+local M = {}
+
+function M.collect_locals(bufnr, query_kind)
+ query_kind = query_kind or 'locals'
+
+ local lang = parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, "ft"))
+ if not lang then return end
+
+ local query = queries.get_query(lang, query_kind)
if not query then return end
- local parser = utils.get_parser(bufnr, ft)
+ local parser = parsers.get_parser(bufnr, lang)
if not parser then return end
local root = parser:parse():root()
@@ -33,18 +46,20 @@ function M.collect_locals(bufnr)
return locals
end
-local function update_cached_locals(bufnr, changed_tick)
- M.locals[bufnr] = {tick=changed_tick, cache=( M.collect_locals(bufnr) or {} )}
+local function update_cached_locals(bufnr, changed_tick, query_kind)
+ query_cache[query_kind][bufnr] = {tick=changed_tick, cache=( M.collect_locals(bufnr, query_kind) or {} )}
end
-function M.get_locals(bufnr)
+function M.get_locals(bufnr, query_kind)
+ query_kind = query_kind or 'locals'
+
local bufnr = bufnr or api.nvim_get_current_buf()
- local cached_local = M.locals[bufnr]
- if not cached_local or api.nvim_buf_get_changedtick(bufnr) < cached_local.tick then
- update_cached_locals(bufnr,api.nvim_buf_get_changedtick(bufnr))
+ local cached_local = query_cache[query_kind][bufnr]
+ if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then
+ update_cached_locals(bufnr,api.nvim_buf_get_changedtick(bufnr), query_kind)
end
- return M.locals[bufnr].cache
+ return query_cache[query_kind][bufnr].cache
end
function M.get_definitions(bufnr)
@@ -89,4 +104,27 @@ function M.get_references(bufnr)
return refs
end
+--- Return all nodes in locals corresponding to a specific capture (like @scope, @reference)
+-- Works like M.get_references or M.get_scopes except you can choose the capture
+-- Can also be a nested capture like @definition.function to get all nodes defining a function
+function M.get_capture_matches(bufnr, capture_string, query_kind)
+ if not string.sub(capture_string, 1,2) == '@' then
+ print('capture_string must start with "@"')
+ return
+ end
+
+ --remove leading "@"
+ capture_string = string.sub(capture_string, 2)
+
+ local matches = {}
+ for _, match in pairs(M.get_locals(bufnr, query_kind)) do
+ local insert = utils.get_at_path(match, capture_string..'.node')
+
+ if insert then
+ table.insert(matches, insert)
+ end
+ end
+ return matches
+end
+
return M
diff --git a/lua/nvim-treesitter/parsers.lua b/lua/nvim-treesitter/parsers.lua
index 07f3e9d34..7fa09df91 100644
--- a/lua/nvim-treesitter/parsers.lua
+++ b/lua/nvim-treesitter/parsers.lua
@@ -1,18 +1,270 @@
local api = vim.api
local ts = vim.treesitter
-local M = {}
+local list = {}
+
+list.javascript = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-javascript",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.c = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-c",
+ files = { "src/parser.c" }
+ }
+}
+
+list.cpp = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-cpp",
+ files = { "src/parser.c", "src/scanner.cc" }
+ }
+}
+
+list.rust = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-rust",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.lua = {
+ install_info = {
+ url = "https://github.com/nvim-treesitter/tree-sitter-lua",
+ files = { "src/parser.c", "src/scanner.cc" }
+ }
+}
+
+list.python = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-python",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.go = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-go",
+ files = { "src/parser.c" },
+ }
+}
+
+list.ruby = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-ruby",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.bash = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-bash",
+ files = { "src/parser.c", "src/scanner.cc" },
+ },
+ used_by = { "zsh" },
+ filetype = 'sh'
+}
+
+list.php = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-php",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.java = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-java",
+ files = { "src/parser.c" },
+ }
+}
+
+list.html = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-html",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.julia = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-julia",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.json = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-json",
+ files = { "src/parser.c" },
+ }
+}
+
+list.css = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-css",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.ocaml = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-ocaml",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.swift = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-swift",
+ files = { "src/parser.c" },
+ }
+}
+
+list.c_sharp = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-c-sharp",
+ files = { "src/parser.c", "src/scanner.c" },
+ },
+ filetype = 'cs'
+}
+
+list.typescript = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-typescript",
+ files = { "src/parser.c", "src/scanner.c" },
+ location = "tree-sitter-typescript/typescript"
+ }
+}
+
+list.tsx = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-typescript",
+ files = { "src/parser.c", "src/scanner.c" },
+ location = "tree-sitter-tsx/tsx"
+ },
+ filetype = 'typescriptreact'
+}
+
+list.scala = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-scala",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.haskell = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-haskell",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.markdown = {
+ install_info = {
+ url = "https://github.com/ikatyang/tree-sitter-markdown",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.toml = {
+ install_info = {
+ url = "https://github.com/ikatyang/tree-sitter-toml",
+ files = { "src/parser.c", "src/scanner.c" },
+ }
+}
+
+list.vue = {
+ install_info = {
+ url = "https://github.com/ikatyang/tree-sitter-vue",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.elm = {
+ install_info = {
+ url = "https://github.com//razzeee/tree-sitter-elm",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.yaml = {
+ install_info = {
+ url = "https://github.com/ikatyang/tree-sitter-yaml",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.nix = {
+ install_info = {
+ url = "https://github.com/cstrahan/tree-sitter-nix",
+ files = { "src/parser.c", "src/scanner.cc" },
+ }
+}
+
+list.regex = {
+ install_info = {
+ url = "https://github.com/tree-sitter/tree-sitter-regex",
+ files = { "src/parser.c" }
+ }
+}
+
+local M = {
+ list = list
+}
+
+local ft_to_parsername = {}
+
+for name, obj in pairs(M.list) do
+ if type(obj.used_by) == 'table' then
+ for _, ft in pairs(obj.used_by) do
+ ft_to_parsername[ft] = name
+ end
+ end
+ ft_to_parsername[obj.filetype or name] = name
+end
+
+function M.ft_to_lang(ft)
+ return ft_to_parsername[ft]
+end
+
+function M.lang_to_ft(lang)
+ local obj = M.list[lang]
+ return vim.tbl_flatten({{obj.filetype or lang}, obj.used_by or {}})
+end
+
+function M.lang_match_ft(lang, ft)
+ for _, f in pairs(M.lang_to_ft(lang)) do
+ if ft == f then
+ return true
+ end
+ end
+ return false
+end
+
+function M.available_parsers()
+ return vim.tbl_keys(M.list)
+end
+
+function M.get_parser_configs()
+ return M.list
+end
function M.has_parser(lang)
- local lang = lang or api.nvim_buf_get_option(0, 'filetype')
+ local buf = api.nvim_get_current_buf()
+ local lang = lang or M.ft_to_lang(api.nvim_buf_get_option(buf, 'ft'))
if not lang or #lang == 0 then return false end
return #api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0
end
function M.get_parser(bufnr, lang)
+ local buf = bufnr or api.nvim_get_current_buf()
+ local lang = lang or M.ft_to_lang(api.nvim_buf_get_option(buf, 'ft'))
+
if M.has_parser(lang) then
- local buf = bufnr or api.nvim_get_current_buf()
- local lang = lang or api.nvim_buf_get_option(buf, 'ft')
if not M[buf] then
M[buf] = {}
end
diff --git a/lua/nvim-treesitter/query.lua b/lua/nvim-treesitter/query.lua
index 644c33933..72cead9dd 100644
--- a/lua/nvim-treesitter/query.lua
+++ b/lua/nvim-treesitter/query.lua
@@ -3,14 +3,71 @@ local ts = vim.treesitter
local M = {}
-local function read_query_file(fname)
- return table.concat(vim.fn.readfile(fname), '\n')
+local function read_query_files(filenames)
+ local contents = {}
+
+ for _,filename in ipairs(filenames) do
+ vim.list_extend(contents, vim.fn.readfile(filename))
+ end
+
+ return table.concat(contents, '\n')
end
-function M.get_query(ft, query_name)
- local query_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', ft, query_name), false)
+-- Creates a function that checks whether a certain query exists
+-- for a specific language.
+local function get_query_guard(query)
+ return function(lang)
+ return M.get_query(lang, query) ~= nil
+ end
+end
+
+-- Some treesitter grammars extend others.
+-- We can use that to import the queries of the base language
+M.base_language_map = {
+ cpp = {'c'},
+ typescript = {'javascript'},
+ tsx = {'typescript', 'javascript'},
+}
+
+M.query_extensions = {
+ javascript = { 'jsx' },
+ tsx = {'javascript.jsx'}
+}
+
+M.has_locals = get_query_guard('locals')
+M.has_highlights = get_query_guard('highlights')
+
+function M.get_query(lang, query_name)
+ local query_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', lang, query_name), true)
+ local query_string = ''
+
if #query_files > 0 then
- return ts.parse_query(ft, read_query_file(query_files[1]))
+ query_string = read_query_files(query_files) .. "\n" .. query_string
+ end
+
+ for _, base_lang in ipairs(M.base_language_map[lang] or {}) do
+ local base_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', base_lang, query_name), true)
+ if base_files and #base_files > 0 then
+ query_string = read_query_files(base_files) .. "\n" .. query_string
+ end
+ end
+
+ local extensions = M.query_extensions[lang]
+ for _, ext in ipairs(extensions or {}) do
+ local l = lang
+ local e = ext
+ if e:match('%.') ~= nil then
+ l = e:match('.*%.'):sub(0, -2)
+ e = e:match('%..*'):sub(2, -1)
+ end
+ local ext_files = api.nvim_get_runtime_file(string.format('queries/%s/%s.scm', l, e), true)
+ if ext_files and #ext_files > 0 then
+ query_string = read_query_files(ext_files) .. "\n" .. query_string
+ end
+ end
+
+ if #query_string > 0 then
+ return ts.parse_query(lang, query_string)
end
end
diff --git a/lua/nvim-treesitter/refactor/highlight_definitions.lua b/lua/nvim-treesitter/refactor/highlight_definitions.lua
new file mode 100644
index 000000000..a581e37d5
--- /dev/null
+++ b/lua/nvim-treesitter/refactor/highlight_definitions.lua
@@ -0,0 +1,77 @@
+-- This module highlights reference usages and the corresponding
+-- definition on cursor hold.
+
+local ts_utils = require'nvim-treesitter.ts_utils'
+local locals = require'nvim-treesitter.locals'
+local api = vim.api
+local cmd = api.nvim_command
+
+local M = {}
+
+local usage_namespace = api.nvim_create_namespace('nvim-treesitter-usages')
+
+function M.highlight_usages(bufnr)
+ M.clear_usage_highlights(bufnr)
+
+ local node_at_point = ts_utils.get_node_at_cursor()
+ local references = locals.get_references(bufnr)
+
+ if not node_at_point or not vim.tbl_contains(references, node_at_point) then
+ return
+ end
+
+ local def_node, scope = ts_utils.find_definition(node_at_point, bufnr)
+ local usages = ts_utils.find_usages(node_at_point, scope)
+
+ for _, usage_node in ipairs(usages) do
+ local start_row, start_col, _, end_col = usage_node:range()
+
+ if usage_node ~= node_at_point then
+ api.nvim_buf_add_highlight(
+ bufnr,
+ usage_namespace,
+ 'TSDefinitionUsage',
+ start_row,
+ start_col,
+ end_col)
+ end
+ end
+
+ if def_node ~= node_at_point then
+ local start_row, start_col, _, end_col = def_node:range()
+
+ api.nvim_buf_add_highlight(
+ bufnr,
+ usage_namespace,
+ 'TSDefinition',
+ start_row,
+ start_col,
+ end_col)
+ end
+end
+
+function M.clear_usage_highlights(bufnr)
+ api.nvim_buf_clear_namespace(bufnr, usage_namespace, 0, -1)
+end
+
+function M.attach(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+
+ cmd(string.format('augroup NvimTreesitterUsages_%d', bufnr))
+ cmd 'au!'
+ -- luacheck: push ignore 631
+ cmd(string.format([[autocmd CursorHold lua require'nvim-treesitter.refactor.highlight_definitions'.highlight_usages(%d)]], bufnr, bufnr))
+ cmd(string.format([[autocmd CursorMoved lua require'nvim-treesitter.refactor.highlight_definitions'.clear_usage_highlights(%d)]], bufnr, bufnr))
+ cmd(string.format([[autocmd InsertEnter lua require'nvim-treesitter.refactor.highlight_definitions'.clear_usage_highlights(%d)]], bufnr, bufnr))
+ -- luacheck: pop
+ cmd 'augroup END'
+end
+
+function M.detach(bufnr)
+ M.clear_usage_highlights(bufnr)
+ cmd(string.format('autocmd! NvimTreesitterUsages_%d CursorHold', bufnr))
+ cmd(string.format('autocmd! NvimTreesitterUsages_%d CursorMoved', bufnr))
+ cmd(string.format('autocmd! NvimTreesitterUsages_%d InsertEnter', bufnr))
+end
+
+return M
diff --git a/lua/nvim-treesitter/refactor/navigation.lua b/lua/nvim-treesitter/refactor/navigation.lua
new file mode 100644
index 000000000..f2eda367c
--- /dev/null
+++ b/lua/nvim-treesitter/refactor/navigation.lua
@@ -0,0 +1,68 @@
+-- Definition based navigation module
+
+local ts_utils = require'nvim-treesitter.ts_utils'
+local locals = require'nvim-treesitter.locals'
+local configs = require'nvim-treesitter.configs'
+local api = vim.api
+
+local M = {}
+
+function M.goto_definition(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local node_at_point = ts_utils.get_node_at_cursor()
+
+ if not node_at_point then return end
+
+ local definition, _ = ts_utils.find_definition(node_at_point, bufnr)
+ local start_row, start_col, _ = definition:start()
+
+ api.nvim_win_set_cursor(0, { start_row + 1, start_col })
+end
+
+function M.list_definitions(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local definitions = locals.get_definitions(bufnr)
+
+ if #definitions < 1 then return end
+
+ local qf_list = {}
+
+ for _, def in ipairs(definitions) do
+ ts_utils.recurse_local_nodes(def, function(_, node, _, match)
+ local lnum, col, _ = node:start()
+
+ table.insert(qf_list, {
+ bufnr = bufnr,
+ lnum = lnum + 1,
+ col = col + 1,
+ text = ts_utils.get_node_text(node)[1] or "",
+ kind = match and match:sub(1, 1) or ""
+ })
+ end)
+ end
+
+ vim.fn.setqflist(qf_list, 'r')
+ api.nvim_command('copen')
+end
+
+function M.attach(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+
+ local config = configs.get_module('refactor.navigation')
+
+ for fn_name, mapping in pairs(config.keymaps) do
+ local cmd = string.format([[:lua require'nvim-treesitter.refactor.navigation'.%s(%d)]], fn_name, bufnr)
+
+ api.nvim_buf_set_keymap(bufnr, 'n', mapping, cmd, { silent = true })
+ end
+end
+
+function M.detach(bufnr)
+ local config = configs.get_module('refactor.navigation')
+
+ for _, mapping in pairs(config.keymaps) do
+ api.nvim_buf_del_keymap(bufnr, 'n', mapping)
+ end
+end
+
+return M
diff --git a/lua/nvim-treesitter/refactor/smart_rename.lua b/lua/nvim-treesitter/refactor/smart_rename.lua
new file mode 100644
index 000000000..e5ee37ac1
--- /dev/null
+++ b/lua/nvim-treesitter/refactor/smart_rename.lua
@@ -0,0 +1,66 @@
+-- Binds a keybinding to smart rename definitions and usages.
+-- Can be used directly using the `smart_rename` function.
+
+local ts_utils = require'nvim-treesitter.ts_utils'
+local configs = require'nvim-treesitter.configs'
+local utils = require'nvim-treesitter.utils'
+local api = vim.api
+
+local M = {}
+
+function M.smart_rename(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local node_at_point = ts_utils.get_node_at_cursor()
+
+ if not node_at_point then
+ utils.print_warning("No node to rename!")
+ return
+ end
+
+ local node_text = ts_utils.get_node_text(node_at_point)[1]
+ local new_name = vim.fn.input('New name: ', node_text or '')
+
+ -- Empty name cancels the interaction or ESC
+ if not new_name or #new_name < 1 then return end
+
+ local definition, scope = ts_utils.find_definition(node_at_point, bufnr)
+ local nodes_to_rename = ts_utils.find_usages(node_at_point, scope)
+
+ if not vim.tbl_contains(nodes_to_rename, node_at_point) then
+ table.insert(nodes_to_rename, node_at_point)
+ end
+
+ if not vim.tbl_contains(nodes_to_rename, definition) then
+ table.insert(nodes_to_rename, definition)
+ end
+
+ for _, node in ipairs(nodes_to_rename) do
+ local start_row, start_col, _, end_col = node:range()
+ local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1]
+
+ if line then
+ local new_line = line:sub(1, start_col) .. new_name .. line:sub(end_col + 1, -1)
+ api.nvim_buf_set_lines(bufnr, start_row, start_row + 1, false, { new_line })
+ end
+ end
+end
+
+function M.attach(bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local config = configs.get_module('refactor.smart_rename')
+
+ for fn_name, mapping in pairs(config.keymaps) do
+ local cmd = string.format([[:lua require'nvim-treesitter.refactor.smart_rename'.%s(%d)]], fn_name, bufnr)
+ api.nvim_buf_set_keymap(bufnr, 'n', mapping, cmd, { silent = true })
+ end
+end
+
+function M.detach(bufnr)
+ local config = configs.get_module('refactor.smart_rename')
+
+ for _, mapping in pairs(config.keymaps) do
+ api.nvim_buf_del_keymap(bufnr, 'n', mapping)
+ end
+end
+
+return M
diff --git a/lua/nvim-treesitter/state.lua b/lua/nvim-treesitter/state.lua
deleted file mode 100644
index adff7eeaf..000000000
--- a/lua/nvim-treesitter/state.lua
+++ /dev/null
@@ -1,122 +0,0 @@
-local api = vim.api
-
-local utils = require'nvim-treesitter.utils'
-
-local M = {}
-
-local buffers = {}
-
-local g_mode = api.nvim_get_mode().mode
-
-local function get_selection_range()
- local _, vstart_row, vstart_col, _ = unpack(vim.fn.getpos("v"))
- local _, cursor_row, cursor_col, _ = unpack(vim.fn.getpos("."))
- if vstart_row < cursor_row then
- return vstart_row, vstart_col, cursor_row, cursor_col
- else
- return cursor_row, cursor_col, vstart_row, vstart_col
- end
-end
-
-function M.update()
- local bufnr = api.nvim_get_current_buf()
- local buf_config = buffers[bufnr]
- if not buf_config then return end
-
- local mode = api.nvim_get_mode().mode
- local cursor = api.nvim_win_get_cursor(0)
- local row = cursor[1]
- local col = cursor[2]
- if row == buf_config.cursor_pos.row
- and col == buf_config.cursor_pos.col
- and mode == g_mode
- then
- return
- end
-
- local root = buf_config.parser.tree:root()
- if not root then return end
-
- local new_node = root:named_descendant_for_range(row - 1, col, row - 1, col)
-
- if new_node ~= buf_config.current_node then
- buf_config.current_node = new_node
- end
-
- -- We only want to update the range when the incremental selection has not started yet
- if mode == "v" and #buf_config.selection.nodes == 0 then
- local row_start, col_start, row_end, col_end = get_selection_range()
- buf_config.selection.range = { row_start, col_start, row_end, col_end }
- elseif mode ~= "v" then
- buf_config.selection.nodes = {}
- buf_config.selection.range = nil
- end
-
- g_mode = mode
- buf_config.cursor_pos.row = row
- buf_config.cursor_pos.col = col
-end
-
-function M.insert_selection_node(bufnr, range)
- local buf_config = buffers[bufnr]
- if not buf_config then return end
-
- table.insert(buffers[bufnr].selection.nodes, range)
-end
-
-function M.pop_selection_node(bufnr)
- local buf_config = buffers[bufnr]
- if not buf_config then return end
-
- table.remove(
- buffers[bufnr].selection.nodes,
- #buffers[bufnr].selection.nodes
- )
-end
-
-function M.run_update()
- local cmd = "lua require'nvim-treesitter.state'.update()"
- api.nvim_command('autocmd NvimTreesitter CursorMoved * '..cmd)
-end
-
-function M.attach_to_buffer(ft)
- local bufnr = api.nvim_get_current_buf()
- local ft = ft or api.nvim_buf_get_option(bufnr, 'ft')
-
- if buffers[bufnr] then return end
-
- local parser = utils.get_parser(bufnr, ft)
- if not parser then return end
-
- buffers[bufnr] = {
- cursor_pos = {},
- current_node = nil,
- selection = {
- range = nil,
- nodes = {}
- },
- parser = parser,
- }
-
- M.update()
- api.nvim_buf_attach(bufnr, false, {
- -- TODO(kyazdani): on lines should only parse the changed content
- -- TODO(kyazdani): add a timer to avoid too frequent updates
- on_lines = function(_, buf) buffers[buf].parser:parse() end,
- on_detach = function(bufnr) buffers[bufnr] = nil end,
- })
-end
-
-function M.get_buf_state(bufnr)
- return buffers[bufnr]
-end
-
-function M.exposed_state(bufnr)
- local buf_state = buffers[bufnr]
- return {
- cursor_pos = buf_state.cursor_pos,
- current_node = buf_state.current_node
- }
-end
-
-return M
diff --git a/lua/nvim-treesitter/ts_utils.lua b/lua/nvim-treesitter/ts_utils.lua
index 1445418e0..7d5e97cdd 100644
--- a/lua/nvim-treesitter/ts_utils.lua
+++ b/lua/nvim-treesitter/ts_utils.lua
@@ -1,6 +1,7 @@
local api = vim.api
local locals = require'nvim-treesitter.locals'
+local parsers = require'nvim-treesitter.parsers'
local M = {}
@@ -14,14 +15,16 @@ function M.get_node_text(node, bufnr)
-- We have to remember that end_col is end-exclusive
local start_row, start_col, end_row, end_col = node:range()
+
if start_row ~= end_row then
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row+1, false)
lines[1] = string.sub(lines[1], start_col+1)
lines[#lines] = string.sub(lines[#lines], 1, end_col)
return lines
else
- local line = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1]
- return { string.sub(line, start_col+1, end_col) }
+ local line = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, false)[1]
+ -- If line is nil then the line is empty
+ return line and { string.sub(line, start_col+1, end_col) } or {}
end
end
@@ -205,4 +208,121 @@ function M.previous_scope(node)
end
end
+function M.get_node_at_cursor(winnr)
+ local cursor = api.nvim_win_get_cursor(winnr or 0)
+ local root = parsers.get_parser().tree:root()
+ return root:named_descendant_for_range(cursor[1]-1,cursor[2],cursor[1]-1,cursor[2])
+end
+
+-- Finds the definition node and it's scope node of a node
+-- @param node starting node
+-- @param bufnr buffer
+-- @returns the definition node and the definition nodes scope node
+function M.find_definition(node, bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local node_text = M.get_node_text(node)[1]
+ local current_scope = M.containing_scope(node)
+ local matching_def_nodes = {}
+
+ -- If a scope wasn't found then use the root node
+ if current_scope == node then
+ current_scope = parsers.get_parser(bufnr).tree:root()
+ end
+
+ -- Get all definitions that match the node text
+ for _, def in ipairs(locals.get_definitions(bufnr)) do
+ for _, def_node in ipairs(M.get_local_nodes(def)) do
+ if M.get_node_text(def_node)[1] == node_text then
+ table.insert(matching_def_nodes, def_node)
+ end
+ end
+ end
+
+ -- Continue up each scope until we find the scope that contains the definition
+ while current_scope do
+ for _, def_node in ipairs(matching_def_nodes) do
+ if M.is_parent(current_scope, def_node) then
+ return def_node, current_scope
+ end
+ end
+ current_scope = M.containing_scope(current_scope:parent())
+ end
+
+ return node, parsers.get_parser(bufnr).tree:root()
+end
+
+-- Gets all nodes from a local list result.
+-- @param local_def the local list result
+-- @returns a list of nodes
+function M.get_local_nodes(local_def)
+ local result = {}
+
+ M.recurse_local_nodes(local_def, function(_, node)
+ table.insert(result, node)
+ end)
+
+ return result
+end
+
+-- Recurse locals results until a node is found.
+-- The accumulator function is given
+-- * The table of the node
+-- * The node
+-- * The full definition match `@definition.var.something` -> 'var.something'
+-- * The last definition match `@definition.var.something` -> 'something'
+-- @param The locals result
+-- @param The accumulator function
+-- @param The full match path to append to
+-- @param The last match
+function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
+ if local_def.node then
+ accumulator(local_def, local_def.node, full_match, last_match)
+ else
+ for match_key, def in pairs(local_def) do
+ M.recurse_local_nodes(
+ def,
+ accumulator,
+ full_match and (full_match..'.'..match_key) or match_key,
+ match_key)
+ end
+ end
+end
+
+-- Finds usages of a node in a given scope
+-- @param node the node to find usages for
+-- @param scope_node the node to look within
+-- @returns a list of nodes
+function M.find_usages(node, scope_node, bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+ local node_text = M.get_node_text(node)[1]
+
+ if not node_text or #node_text < 1 then return {} end
+
+ local scope_node = scope_node or parsers.get_parser(bufnr).tree:root()
+ local references = locals.get_references(bufnr)
+ local usages = {}
+
+ M.recurse_tree(scope_node, function(iter_node, _, next)
+ if vim.tbl_contains(references, iter_node) and M.get_node_text(iter_node)[1] == node_text then
+ table.insert(usages, iter_node)
+ end
+ next()
+ end)
+
+ return usages
+end
+
+-- Recurses all child nodes of a tree.
+-- The callback is provided the child node, parent_node, and a callback to recurse into
+-- the child node. This allows for the ability to short circuit the recursion
+-- if we found what we are looking for, we can then stop the recursion or skip a node
+-- if need be.
+-- @param tree the node root
+-- @param cb the callback for each node
+function M.recurse_tree(tree, cb)
+ for _, child in ipairs(M.get_named_children(tree)) do
+ cb(child, tree, function(next_node) M.recurse_tree(next_node or child, cb) end)
+ end
+end
+
return M
diff --git a/lua/nvim-treesitter/utils.lua b/lua/nvim-treesitter/utils.lua
index 9dc5d17bd..c87cee2d8 100644
--- a/lua/nvim-treesitter/utils.lua
+++ b/lua/nvim-treesitter/utils.lua
@@ -1,7 +1,6 @@
local api = vim.api
local fn = vim.fn
local luv = vim.loop
-local ts = vim.treesitter
local M = {}
@@ -45,23 +44,27 @@ function M.get_cache_dir()
return nil, 'Invalid cache rights, $XDG_CACHE_HOME or /tmp should be read/write'
end
-function M.has_parser(lang)
- local lang = lang or api.nvim_buf_get_option(0, 'filetype')
- return #api.nvim_get_runtime_file('parser/' .. lang .. '.so', false) > 0
+-- Gets a property at path
+-- @param tbl the table to access
+-- @param path the '.' seperated path
+-- @returns the value at path or nil
+function M.get_at_path(tbl, path)
+ local segments = vim.split(path, '.', true)
+ local result = tbl
+
+ for _, segment in ipairs(segments) do
+ if type(result) == 'table' then
+ result = result[segment]
+ end
+ end
+
+ return result
end
-function M.get_parser(bufnr, lang)
- if M.has_parser() then
- local buf = bufnr or api.nvim_get_current_buf()
- local lang = lang or api.nvim_buf_get_option(buf, 'ft')
- if not M[buf] then
- M[buf] = {}
- end
- if not M[buf][lang] then
- M[buf][lang] = ts.get_parser(buf, lang)
- end
- return M[buf][lang]
- end
+-- Prints a warning message
+-- @param text the text message
+function M.print_warning(text)
+ api.nvim_command(string.format([[echohl WarningMsg | echo "%s" | echohl None]], text))
end
return M
diff --git a/plugin/nvim-treesitter.vim b/plugin/nvim-treesitter.vim
index 30ee3241d..2bebbba99 100644
--- a/plugin/nvim-treesitter.vim
+++ b/plugin/nvim-treesitter.vim
@@ -1,4 +1,4 @@
-" Last Change: 2020 avril 25
+" Last Change: 2020 Jun 29
if exists('g:loaded_nvim_treesitter')
finish
@@ -11,10 +11,52 @@ let g:loaded_nvim_treesitter = 1
lua << EOF
ts_installable_parsers = function()
- return table.concat(require'nvim-treesitter.configs'.available_parsers(), '\n')
+ return table.concat(require'nvim-treesitter.parsers'.available_parsers(), '\n')
end
ts_available_modules = function()
return table.concat(require'nvim-treesitter.configs'.available_modules(), '\n')
end
require'nvim-treesitter'.setup()
EOF
+
+highlight default link TSError Error
+
+highlight default link TSPunctDelimiter Delimiter
+highlight default link TSPunctBracket Delimiter
+highlight default link TSPunctSpecial Delimiter
+
+highlight default link TSConstant Constant
+highlight default link TSConstBuiltin Special
+highlight default link TSConstMacro Define
+highlight default link TSString String
+highlight default link TSStringRegex String
+highlight default link TSStringEscape SpecialChar
+highlight default link TSCharacter Character
+highlight default link TSNumber Number
+highlight default link TSBoolean Boolean
+highlight default link TSFloat Float
+
+highlight default link TSFunction Function
+highlight default link TSFuncBuiltin Special
+highlight default link TSFuncMacro Macro
+highlight default link TSParameter Identifier
+highlight default link TSMethod Function
+highlight default link TSField Identifier
+highlight default link TSProperty Identifier
+highlight default link TSConstructor Special
+
+highlight default link TSConditional Conditional
+highlight default link TSRepeat Repeat
+highlight default link TSLabel Label
+highlight default link TSOperator Operator
+highlight default link TSKeyword Keyword
+highlight default link TSException Exception
+
+highlight default link TSType Type
+highlight default link TSTypeBuiltin Type
+highlight default link TSStructure Structure
+highlight default link TSInclude Include
+
+highlight default link TSDefinitionUsage Visual
+highlight default link TSDefinition Search
+
diff --git a/queries/bash/highlights.scm b/queries/bash/highlights.scm
new file mode 100644
index 000000000..c0ec8d698
--- /dev/null
+++ b/queries/bash/highlights.scm
@@ -0,0 +1,110 @@
+[
+ "("
+ ")"
+ "{"
+ "}"
+ "["
+ "]"
+ ] @punctuation.bracket
+
+[
+ ";"
+ ";;"
+ (heredoc_start)
+ ] @punctuation.delimiter
+
+[
+ ">"
+ "<"
+ "&"
+ "&&"
+ "|"
+ "||"
+ "="
+ "=="
+ "!="
+ ] @operator
+
+[
+ (string)
+ (raw_string)
+ (heredoc_body)
+] @string
+
+[
+ "if"
+ "then"
+ "else"
+ "elif"
+ "fi"
+ "case"
+ "in"
+ "esac"
+ ] @conditional
+
+[
+ "for"
+ "do"
+ "done"
+ "while"
+ ] @repeat
+
+[
+ "declare"
+ "export"
+ "local"
+ "readonly"
+ "unset"
+ ] @keyword
+
+[
+ (special_variable_name)
+ ("$" (special_variable_name))
+ ] @constant
+
+((word) @constant
+ (#match? @constant "SIG(INT|TERM|QUIT|TIN|TOU|STP|HUP)"))
+
+((word) @boolean
+ (#match? @boolean "true|false"))
+
+((word) @number
+ (#match? @number "^\d*$"))
+
+(comment) @comment
+(test_operator) @string.
+
+(command_substitution
+ [ "$(" ")" ] @punctuation.bracket)
+
+
+(function_definition
+ name: (word) @function)
+
+(command_name (word)) @function
+
+(command
+ argument: [
+ (word) @parameter
+ ((word) @number
+ (#match? @number "^\d*$"))
+ (concatenation (word) @parameter)
+ ])
+
+(file_redirect
+ descriptor: (file_descriptor) @operator
+ destination: (word) @parameter)
+
+
+("$" (variable_name)) @identifier
+
+(expansion
+ [ "${" "}" ] @punctuation.bracket)
+
+(variable_name) @identifier
+
+(case_item
+ value: (word) @parameter)
+
+(concatenation (word) @parameter)
+
diff --git a/queries/c/highlights.scm b/queries/c/highlights.scm
index 4cfb042cc..3fbf4ffa9 100644
--- a/queries/c/highlights.scm
+++ b/queries/c/highlights.scm
@@ -1,52 +1,79 @@
-"break" @keyword
-"case" @conditional
-"const" @keyword
-"continue" @repeat
-"default" @keyword
-"do" @repeat
-"else" @conditional
-"enum" @keyword
-"extern" @keyword
-"for" @repeat
-"if" @conditional
-"inline" @keyword
-"return" @keyword
-"sizeof" @keyword
-"static" @keyword
-"struct" @keyword
-"switch" @keyword
-"typedef" @keyword
-"union" @keyword
-"volatile" @keyword
-"while" @repeat
+[
+ "const"
+ "default"
+ "enum"
+ "extern"
+ "inline"
+ "return"
+ "sizeof"
+ "static"
+ "struct"
+ "typedef"
+ "union"
+ "volatile"
+] @keyword
+
+[
+ "while"
+ "for"
+ "do"
+ "continue"
+ "break"
+] @repeat
+
+[
+ "if"
+ "else"
+ "case"
+ "switch"
+] @conditional
+
+(conditional_expression [ "?" ":" ] @conditional)
"#define" @constant.macro
-"#else" @keyword
-"#endif" @keyword
-"#if" @keyword
-"#ifdef" @keyword
-"#ifndef" @keyword
-"#include" @keyword
-(preproc_directive) @keyword
+[
+ "#if"
+ "#ifdef"
+ "#ifndef"
+ "#else"
+ "#elif"
+ "#endif"
+ "#include"
+ (preproc_directive)
+] @keyword
-"--" @operator
-"-" @operator
-"-=" @operator
-"->" @operator
-"!=" @operator
-"*" @operator
-"&" @operator
-"&&" @operator
-"+" @operator
-"++" @operator
-"+=" @operator
-"<" @operator
-"==" @operator
-">" @operator
-"||" @operator
+[
+ "--"
+ "-"
+ "->"
+ "!="
+ "*"
+ "/"
+ "&"
+ "&&"
+ "+"
+ "++"
+ "<"
+ "<="
+ "=="
+ "="
+ "~"
+ ">"
+ ">="
+ "!"
+ "||"
-"." @delimiter
-";" @delimiter
+ "-="
+ "+="
+ "*="
+ "/="
+ "|="
+ "&="
+] @operator
+
+[ "." ";" ":" "," ] @punctuation.delimiter
+
+[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket
(string_literal) @string
(system_lib_string) @string
@@ -64,6 +91,8 @@
declarator: (identifier) @function)
(preproc_function_def
name: (identifier) @function.macro)
+(preproc_arg) @function.macro
+; TODO (preproc_arg) @embedded
(field_identifier) @property
(statement_identifier) @label
@@ -71,7 +100,19 @@
(primitive_type) @type
(sized_type_specifier) @type
+((identifier) @type
+ (#match? @type "^[A-Z]"))
+
((identifier) @constant
- (match? @constant "^[A-Z][A-Z\\d_]+$"))
+ (#match? @constant "^[A-Z][A-Z0-9_]+$"))
(comment) @comment
+
+;; Parameters
+(parameter_list
+ (parameter_declaration) @parameter)
+
+(preproc_params
+ (identifier)) @parameter
+
+(ERROR) @error
diff --git a/queries/c/locals.scm b/queries/c/locals.scm
index b08d706ee..06b5a4c95 100644
--- a/queries/c/locals.scm
+++ b/queries/c/locals.scm
@@ -17,7 +17,7 @@
(declaration
declarator: (identifier) @definition.var)
(enum_specifier
- name: (*) @definition.type
+ name: (_) @definition.type
(enumerator_list
(enumerator name: (identifier) @definition.var)))
@@ -31,8 +31,11 @@
(identifier) @reference
;; Scope
-(for_statement) @scope
-(if_statement) @scope
-(while_statement) @scope
-(translation_unit) @scope
-(function_definition) @scope
+[
+ (for_statement)
+ (if_statement)
+ (while_statement)
+ (translation_unit)
+ (function_definition)
+ (compound_statement) ; a block in curly braces
+] @scope
diff --git a/queries/cpp/highlights.scm b/queries/cpp/highlights.scm
new file mode 100644
index 000000000..49382bdf7
--- /dev/null
+++ b/queries/cpp/highlights.scm
@@ -0,0 +1,103 @@
+((identifier) @field
+ (#match? @field "^_"))
+
+((identifier) @field
+ (#match? @field "^m_"))
+
+((identifier) @field
+ (#match? @field "_$"))
+
+;(field_expression) @parameter ;; How to highlight this?
+(template_function
+ name: (identifier) @function)
+
+(template_method
+ name: (field_identifier) @method)
+
+(template_function
+ name: (scoped_identifier
+ name: (identifier) @function))
+
+(namespace_identifier) @constant
+
+((namespace_identifier) @type
+ (#match? @type "^[A-Z]"))
+((namespace_identifier) @constant
+ (#match? @constant "^[A-Z][A-Z_0-9]*$"))
+
+(destructor_name
+ name: (_) @function)
+
+(function_declarator
+ declarator: (scoped_identifier
+ name: (identifier) @function))
+((function_declarator
+ declarator: (scoped_identifier
+ name: (identifier) @constructor))
+ (#match? @constructor "^[A-Z]"))
+
+(call_expression
+ function: (scoped_identifier
+ name: (identifier) @function))
+
+(call_expression
+ function: (field_expression
+ field: (field_identifier) @function))
+
+((call_expression
+ function: (scoped_identifier
+ name: (identifier) @constructor))
+(#match? @constructor "^[A-Z]"))
+
+((call_expression
+ function: (field_expression
+ field: (field_identifier) @constructor))
+(#match? @constructor "^[A-Z]"))
+
+;; constructing a type in a intizializer list: Constructor (): **SuperType (1)**
+((field_initializer
+ (field_identifier) @constructor
+ (argument_list))
+ (#match? @constructor "^[A-Z]"))
+
+
+; Constants
+
+(this) @constant.builtin
+(nullptr) @constant
+
+(true) @boolean
+(false) @boolean
+
+; Keywords
+
+[
+ "try"
+ "catch"
+ "noexcept"
+ "throw"
+] @exception
+
+
+[
+ "class"
+ "constexpr"
+ "delete"
+ "explicit"
+ "final"
+ "friend"
+ "mutable"
+ "namespace"
+ "new"
+ "override"
+ "private"
+ "protected"
+ "public"
+ "template"
+ "typename"
+ "using"
+ "virtual"
+ (auto)
+] @keyword
+
+"::" @operator
diff --git a/queries/cpp/locals.scm b/queries/cpp/locals.scm
new file mode 100644
index 000000000..47b69c866
--- /dev/null
+++ b/queries/cpp/locals.scm
@@ -0,0 +1,51 @@
+
+;; Class / struct defintions
+(class_specifier) @scope
+(struct_specifier) @scope
+
+(reference_declarator
+ (identifier) @definition.var)
+
+(struct_specifier
+ name: (type_identifier) @definition.type)
+
+(struct_specifier
+ name: (scoped_type_identifier
+ name: (type_identifier) @definition.type))
+
+(class_specifier
+ name: (type_identifier) @definition.type)
+
+(class_specifier
+ name: (scoped_type_identifier
+ name: (type_identifier) @definition.type))
+
+;; Function defintions
+(template_function
+ name: (identifier) @definition.function) @scope
+
+(template_method
+ name: (field_identifier) @definition.method) @scope
+
+(template_function
+ name: (scoped_identifier
+ name: (identifier) @definition.function)) @scope
+
+(function_declarator
+ declarator: (scoped_identifier
+ name: (type_identifier) @definition.function)) @scope
+
+(field_declaration
+ declarator: (function_declarator
+ (field_identifier) @definition.method))
+
+(lambda_expression) @scope
+
+;; Control structures
+(try_statement
+ body: (_) @scope)
+
+(catch_clause) @scope
+
+(destructor_name
+ name: (_) @constructor)
diff --git a/queries/css/highlights.scm b/queries/css/highlights.scm
index 72009e765..1b876c4b1 100644
--- a/queries/css/highlights.scm
+++ b/queries/css/highlights.scm
@@ -1,72 +1,91 @@
-"@media" @keyword
-"@import" @include
-"@charset" @keyword
-"@namespace" @keyword
-"@supports" @keyword
-"@keyframes" @keyword
-(at_keyword) @keyword
-(to) @keyword
-(from) @keyword
-(important) @keyword
+[
+ "@media"
+ "@import"
+ "@charset"
+ "@namespace"
+ "@supports"
+ "@keyframes"
+ (at_keyword)
+ (to)
+ (from)
+ (important)
+ ] @keyword
(comment) @comment
-(tag_name) @type
-(nesting_selector) @type
-(universal_selector) @type
+[
+ (tag_name)
+ (nesting_selector)
+ (universal_selector)
+ ] @type
(function_name) @function
-"~" @operator
-">" @operator
-"+" @operator
-"-" @operator
-"*" @operator
-"/" @operator
-"=" @operator
-"^=" @operator
-"|=" @operator
-"~=" @operator
-"$=" @operator
-"*=" @operator
+[
+ "~"
+ ">"
+ "+"
+ "-"
+ "*"
+ "/"
+ "="
+ "^="
+ "|="
+ "~="
+ "$="
+ "*="
+ "and"
+ "or"
+ "not"
+ "only"
+ ] @operator
-"and" @operator
-"or" @operator
-"not" @operator
-"only" @operator
(attribute_selector (plain_value) @string)
(pseudo_element_selector (tag_name) @property)
(pseudo_class_selector (class_name) @property)
-(class_name) @property
-(id_name) @property
-(namespace_name) @property
-(property_name) @property
-(feature_name) @property
-(attribute_name) @property
+[
+ (class_name)
+ (id_name)
+ (namespace_name)
+ (property_name)
+ (feature_name)
+ (attribute_name)
+ ] @property
((property_name) @type
- (#match? @type "^--"))
+ (#match? @type "^--"))
((plain_value) @type
- (#match? @type "^--"))
+ (#match? @type "^--"))
-(string_value) @string
-(color_value) @string
-(identifier) @string
+[
+ (string_value)
+ (color_value)
+ (identifier)
+ (unit)
+ ] @string
-(integer_value) @number
-(float_value) @number
-(unit) @string
+[
+ (integer_value)
+ (float_value)
+ ] @number
-"#" @punctuation.delimiter
-"," @punctuation.delimiter
-"." @punctuation.delimiter
-":" @punctuation.delimiter
-"::" @punctuation.delimiter
-";" @punctuation.delimiter
-"{" @punctuation.bracket
-")" @punctuation.bracket
-"(" @punctuation.bracket
-"}" @punctuation.bracket
+[
+ "#"
+ ","
+ "."
+ ":"
+ "::"
+ ";"
+ ] @punctuation.delimiter
+
+[
+ "{"
+ ")"
+ "("
+ "}"
+ ] @punctuation.bracket
+
+(ERROR) @error
diff --git a/queries/html/highlights.scm b/queries/html/highlights.scm
index 582e1e46f..1e83ecf4c 100644
--- a/queries/html/highlights.scm
+++ b/queries/html/highlights.scm
@@ -8,8 +8,9 @@
"=" @operator
-"<" @punctuation.bracket
-">" @punctuation.bracket
-"" @punctuation.bracket
-"/>" @punctuation.bracket
-
+[
+ "<"
+ ">"
+ ""
+ "/>"
+ ] @punctuation.bracket
diff --git a/queries/java/highlights.scm b/queries/java/highlights.scm
new file mode 100644
index 000000000..2b41f1d53
--- /dev/null
+++ b/queries/java/highlights.scm
@@ -0,0 +1,214 @@
+; CREDITS @maxbrunsfeld (maxbrunsfeld@gmail.com)
+
+; Methods
+
+
+(method_declaration
+ name: (identifier) @method)
+(method_invocation
+ name: (identifier) @method)
+
+(super) @function.builtin
+
+; Parameters
+(formal_parameter
+ name: (identifier) @parameter)
+
+;; Lambda parameter
+(inferred_parameters (identifier) @parameter) ; (x,y) -> ...
+(lambda_expression
+ parameters: (identifier) @parameter) ; x -> ...
+
+
+; Annotations
+
+
+(annotation
+ name: (identifier) @attribute)
+(marker_annotation
+ name: (identifier) @attribute)
+
+
+; Operators
+
+[
+"@"
+"+"
+"?"
+":"
+"++"
+"-"
+"--"
+"&"
+"&&"
+"|"
+"||"
+"!="
+"=="
+"*"
+"/"
+"%"
+"<"
+"<="
+">"
+">="
+"="
+"-="
+"+="
+"*="
+"/="
+"%="
+"->"
+"^"
+"^="
+"&="
+"|="
+"~"
+">>"
+">>>"
+"<<"
+"::"
+] @operator
+
+; Types
+
+(interface_declaration
+ name: (identifier) @type)
+(class_declaration
+ name: (identifier) @type)
+(enum_declaration
+ name: (identifier) @type)
+(constructor_declaration
+ name: (identifier) @type)
+(type_identifier) @type
+
+
+
+((field_access
+ object: (identifier) @type)
+ (#match? @type "^[A-Z]"))
+((scoped_identifier
+ scope: (identifier) @type)
+ (#match? @type "^[A-Z]"))
+
+[
+(boolean_type)
+(integral_type)
+(floating_point_type)
+(void_type)
+] @type.builtin
+
+; Variables
+
+((identifier) @constant
+ (#match? @constant "^_*[A-Z][A-Z\d_]+"))
+
+
+
+; Literals
+
+[
+(hex_integer_literal)
+(decimal_integer_literal)
+(octal_integer_literal)
+(binary_integer_literal)
+] @number
+
+[
+(decimal_floating_point_literal)
+(hex_floating_point_literal)
+] @float
+
+(character_literal) @character
+(string_literal) @string
+(null_literal) @constant.builtin
+
+(comment) @comment
+
+[
+(true)
+(false)
+] @boolean
+
+; Keywords
+
+[
+"abstract"
+"assert"
+"break"
+"catch"
+"class"
+"continue"
+"default"
+"enum"
+"exports"
+"extends"
+"final"
+"finally"
+"implements"
+"instanceof"
+"interface"
+"module"
+"native"
+"new"
+"open"
+"opens"
+"package"
+"private"
+"protected"
+"provides"
+"public"
+"requires"
+"return"
+"static"
+"strictfp"
+"synchronized"
+"throw"
+"throws"
+"to"
+"transient"
+"transitive"
+"try"
+"uses"
+"volatile"
+"with"
+] @keyword
+
+; Conditionals
+
+[
+"if"
+"else"
+"switch"
+"case"
+] @conditional
+
+;
+
+[
+"for"
+"while"
+"do"
+] @repeat
+
+; Includes
+
+"import" @include
+"package" @include
+
+; Punctuation
+
+[
+";"
+"."
+","
+] @punctuation.delimiter
+
+[
+"["
+"]"
+"{"
+"}"
+"("
+")"
+] @punctuation.bracket
diff --git a/queries/java/locals.scm b/queries/java/locals.scm
new file mode 100644
index 000000000..56c629f4e
--- /dev/null
+++ b/queries/java/locals.scm
@@ -0,0 +1,10 @@
+; CREDITS @maxbrunsfeld (maxbrunsfeld@gmail.com)
+
+(class_declaration
+ name: (identifier) @name) @class
+
+(method_declaration
+ name: (identifier) @name) @method
+
+(method_invocation
+ name: (identifier) @name) @call
diff --git a/queries/javascript/highlights.scm b/queries/javascript/highlights.scm
new file mode 100644
index 000000000..dfbabc977
--- /dev/null
+++ b/queries/javascript/highlights.scm
@@ -0,0 +1,190 @@
+; Types
+
+; Javascript
+; Special identifiers
+;--------------------
+
+((identifier) @constant
+ (#match? @constant "^[A-Z_][A-Z\\d_]+$"))
+
+((shorthand_property_identifier) @constant
+ (#match? @constant "^[A-Z_][A-Z\\d_]+$"))
+
+((identifier) @constructor
+ (#match? @constructor "^[A-Z]"))
+
+((identifier) @variable.builtin
+ (#match? @variable.builtin "^(arguments|module|console|window|document)$"))
+
+((identifier) @function.builtin
+ (#eq? @function.builtin "require"))
+
+; Function and method definitions
+;--------------------------------
+
+(function
+ name: (identifier) @function)
+(function_declaration
+ name: (identifier) @function)
+(method_definition
+ name: (property_identifier) @function.method)
+
+(pair
+ key: (property_identifier) @function.method
+ value: (function))
+(pair
+ key: (property_identifier) @function.method
+ value: (arrow_function))
+
+(assignment_expression
+ left: (member_expression
+ property: (property_identifier) @function.method)
+ right: (arrow_function))
+(assignment_expression
+ left: (member_expression
+ property: (property_identifier) @function.method)
+ right: (function))
+
+(variable_declarator
+ name: (identifier) @function
+ value: (arrow_function))
+(variable_declarator
+ name: (identifier) @function
+ value: (function))
+
+(assignment_expression
+ left: (identifier) @function
+ right: (arrow_function))
+(assignment_expression
+ left: (identifier) @function
+ right: (function))
+
+; Function and method calls
+;--------------------------
+
+(call_expression
+ function: (identifier) @function)
+
+(call_expression
+ function: (member_expression
+ property: (property_identifier) @function.method))
+
+; Variables
+;----------
+
+(formal_parameters (identifier) @variable.parameter)
+
+(identifier) @variable
+
+; Properties
+;-----------
+
+(property_identifier) @property
+
+; Literals
+;---------
+
+(this) @variable.builtin
+(super) @variable.builtin
+
+(true) @boolean
+(false) @boolean
+(null) @constant.builtin
+(comment) @comment
+(string) @string
+(regex) @string.special
+(template_string) @string
+(number) @number
+
+; Punctuation
+;------------
+
+(template_substitution
+ "${" @punctuation.special
+ "}" @punctuation.special) @embedded
+
+";" @punctuation.delimiter
+"." @punctuation.delimiter
+"," @punctuation.delimiter
+
+"--" @operator
+"-" @operator
+"-=" @operator
+"&&" @operator
+"+" @operator
+"++" @operator
+"+=" @operator
+"<" @operator
+"<<" @operator
+"=" @operator
+"==" @operator
+"===" @operator
+"=>" @operator
+">" @operator
+">>" @operator
+"||" @operator
+"??" @operator
+
+"(" @punctuation.bracket
+")" @punctuation.bracket
+"[" @punctuation.bracket
+"]" @punctuation.bracket
+"{" @punctuation.bracket
+"}" @punctuation.bracket
+
+; Keywords
+;----------
+
+[
+"if"
+"else"
+"switch"
+"case"
+"default"
+] @conditional
+
+[
+"import"
+"from"
+"as"
+] @include
+
+[
+"for"
+"of"
+"do"
+"while"
+"continue"
+] @repeat
+
+[
+"async"
+"await"
+"break"
+"catch"
+"class"
+"const"
+"debugger"
+"delete"
+"export"
+"extends"
+"finally"
+"function"
+"get"
+"in"
+"instanceof"
+"let"
+"new"
+"return"
+"set"
+"static"
+"switch"
+"target"
+"throw"
+"try"
+"typeof"
+"var"
+"void"
+"with"
+"yield"
+] @keyword
diff --git a/queries/javascript/locals.scm b/queries/javascript/locals.scm
new file mode 100644
index 000000000..d56000d5a
--- /dev/null
+++ b/queries/javascript/locals.scm
@@ -0,0 +1,38 @@
+; Scopes
+;-------
+
+(statement_block) @scope
+(function) @scope
+(arrow_function) @scope
+(function_declaration) @scope
+(method_definition) @scope
+
+; Definitions
+;------------
+
+(formal_parameters
+ (identifier) @definition)
+
+(formal_parameters
+ (object_pattern
+ (identifier) @definition))
+
+(formal_parameters
+ (object_pattern
+ (shorthand_property_identifier) @definition))
+
+(formal_parameters
+ (array_pattern
+ (identifier) @definition))
+
+(variable_declarator
+ name: (identifier) @definition)
+
+(import_specifier
+ (identifier) @definition)
+
+; References
+;------------
+
+(identifier) @reference
+
diff --git a/queries/json/highlights.scm b/queries/json/highlights.scm
new file mode 100644
index 000000000..bdc35ad98
--- /dev/null
+++ b/queries/json/highlights.scm
@@ -0,0 +1,13 @@
+(true) @boolean
+(false) @boolean
+(null) @constant.builtin
+(number) @number
+(pair key: (string) @label)
+(pair value: (string) @string)
+(string_content (escape_sequence) @string.escape)
+(ERROR) @error
+"," @punctuation.delimiter
+"[" @punctuation.bracket
+"]" @punctuation.bracket
+"{" @punctuation.bracket
+"}" @punctuation.bracket
diff --git a/queries/lua/highlights.scm b/queries/lua/highlights.scm
index a674638e8..28247491d 100644
--- a/queries/lua/highlights.scm
+++ b/queries/lua/highlights.scm
@@ -2,57 +2,70 @@
;;; Builtins
;; Keywords
-"local" @keyword
-"if" @conditional
-"then" @conditional
-"else" @conditional
-"elseif" @conditional
-"end" @keyword
-"return" @keyword
-"do" @repeat
-"while" @repeat
-"repeat" @repeat
-"for" @repeat
-(break_statement) @keyword
-"goto" @keyword
+
+[
+"if"
+"then"
+"else"
+"elseif"
+ ] @conditional
+
+[
+"do"
+"while"
+"repeat"
+"for"
+"in"
+] @repeat
+
+[
+"local"
+"end"
+"return"
+(break_statement)
+"goto"
+] @keyword
;; Operators
-"~=" @operator
-"==" @operator
-"<=" @operator
-">=" @operator
-"not" @operator
-"and" @operator
-"or" @operator
-"<" @operator
-">" @operator
+[
+"~="
+"=="
+"<="
+">="
+"not"
+"and"
+"or"
+"<"
+">"
+"+"
+"-"
+"%"
+"/"
+"//"
+"*"
+"^"
+"&"
+"~"
+"|"
+">>"
+"<<"
+".."
+"#"
+ ] @operator
-"+" @operator
-"-" @operator
-"%" @operator
-"/" @operator
-"//" @operator
-"*" @operator
-"^" @operator
-"&" @operator
-"~" @operator
-"|" @operator
-">>" @operator
-"<<" @operator
-".." @operator
-"#" @operator
;; Constants
-(false) @boolean
-(true) @boolean
+[
+(false)
+(true)
+] @boolean
(nil) @constant.builtin
(spread) @constant ;; "..."
;; Nodes
-(function "function" @function "end" @function)
-(function_definition "function" @function "end" @function)
-(local_function "function" @function "end" @function)
-(table "{" @constructor "}" @constructor)
+(_ "function" @function "end" @function) ;; Any node that has both funtion and end in it
+
+(table ["{" "}"] @constructor)
(comment) @comment
(string) @string
(number) @number
diff --git a/queries/lua/locals.scm b/queries/lua/locals.scm
index ee2927328..ca15119e3 100644
--- a/queries/lua/locals.scm
+++ b/queries/lua/locals.scm
@@ -12,6 +12,8 @@
(parameters (identifier) @definition.var)))
((function
(parameters (identifier) @definition.var)))
+((function_definition
+ (parameters (identifier) @definition.var)))
;; Loops
((loop_expression
diff --git a/queries/python/highlights.scm b/queries/python/highlights.scm
index 0b6e30033..fb316bea5 100644
--- a/queries/python/highlights.scm
+++ b/queries/python/highlights.scm
@@ -1,33 +1,38 @@
;; From tree-sitter-python licensed under MIT License
; Copyright (c) 2016 Max Brunsfeld
+; Reset highlighing in f-string interpolations
+(interpolation) @Normal
+
; Identifier naming conventions
-
-
-((import_from_statement
- name: (dotted_name
- (identifier)) @type)
- (match? @type "^[A-Z]"))
-
((identifier) @type
(match? @type "^[A-Z]"))
-
((identifier) @constant
- (match? @constant "^[A-Z][A-Z_]*$"))
+ (match? @constant "^[A-Z][A-Z_0-9]*$"))
+
+((identifier) @constant.builtin
+ (match? @constant.builtin "^__[a-zA-Z0-9_]*__$"))
; Function calls
(decorator) @function
+((decorator (dotted_name (identifier) @function))
+ (match? @function "^([A-Z])@!.*$"))
+
+(call
+ function: (identifier) @function)
(call
function: (attribute
attribute: (identifier) @method))
-(call
- function: (identifier) @function)
+((call
+ function: (identifier) @constructor)
+ (match? @constructor "^[A-Z]"))
((call
- (identifier) @constructor)
+ function: (attribute
+ attribute: (identifier) @constructor))
(match? @constructor "^[A-Z]"))
;; Builtin functions
@@ -43,10 +48,9 @@
(function_definition
name: (identifier) @function)
-(identifier) @variable
-(attribute attribute: (identifier) @property)
(type (identifier) @type)
-((call
+
+((call
function: (identifier) @isinstance
arguments: (argument_list
(*)
@@ -55,7 +59,7 @@
; Normal parameters
(parameters
- (identifier) @parameter)
+ (identifier) @parameter)
; Default parameters
(keyword_argument
name: (identifier) @parameter)
@@ -65,17 +69,16 @@
; Variadic parameters *args, **kwargs
(parameters
(list_splat ; *args
- (identifier) @parameter))
+ (identifier) @parameter))
(parameters
(dictionary_splat ; **kwargs
- (identifier) @parameter))
+ (identifier) @parameter))
; Literals
(none) @constant.builtin
-(true) @boolean
-(false) @boolean
+[(true) (false)] @boolean
((identifier) @constant.builtin
(match? @constant.builtin "self"))
@@ -84,126 +87,104 @@
(comment) @comment
(string) @string
-(escape_sequence) @escape
+(escape_sequence) @string.escape
+
+; Tokens
+
+[
+ "-"
+ "-="
+ ":="
+ "!="
+ "*"
+ "**"
+ "**="
+ "*="
+ "/"
+ "//"
+ "//="
+ "/="
+ "&"
+ "%"
+ "%="
+ "^"
+ "+"
+ "+="
+ "<"
+ "<<"
+ "<="
+ "<>"
+ "="
+ "=="
+ ">"
+ ">="
+ ">>"
+ "|"
+ "~"
+ "and"
+ "in"
+ "is"
+ "not"
+ "or"
+] @operator
+
+; Keywords
+
+[
+ "assert"
+ "async"
+ "await"
+ "class"
+ "def"
+ "del"
+ "except"
+ "exec"
+ "finally"
+ "global"
+ "lambda"
+ "nonlocal"
+ "pass"
+ "print"
+ "raise"
+ "return"
+ "try"
+ "with"
+ "yield"
+] @keyword
+
+[ "as" "from" "import"] @include
+
+[ "if" "elif" "else" ] @conditional
+
+[ "for" "while" "break" "continue" ] @repeat
+
+[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket
(interpolation
"{" @punctuation.special
"}" @punctuation.special) @embedded
-; Tokens
+[ "," "." ":" ] @punctuation.delimiter
-"-" @operator
-"->" @operator
-"-=" @operator
-"!=" @operator
-"*" @operator
-"**" @operator
-"**=" @operator
-"*=" @operator
-"/" @operator
-"//" @operator
-"//=" @operator
-"/=" @operator
-"&" @operator
-"%" @operator
-"%=" @operator
-"^" @operator
-"+" @operator
-"+=" @operator
-"<" @operator
-"<<" @operator
-"<=" @operator
-"<>" @operator
-"=" @operator
-"==" @operator
-">" @operator
-">=" @operator
-">>" @operator
-"|" @operator
-"~" @operator
-"and" @operator
-"in" @operator
-"is" @operator
-"not" @operator
-"or" @operator
-
-; Keywords
-
-"as" @include
-"assert" @keyword
-"async" @keyword
-"await" @keyword
-"break" @repeat
-"class" @keyword
-"continue" @repeat
-"def" @keyword
-"del" @keyword
-"elif" @conditional
-"else" @conditional
-"except" @keyword
-"exec" @keyword
-"finally" @keyword
-"for" @repeat
-"from" @include
-"global" @keyword
-"if" @conditional
-"import" @include
-"lambda" @keyword
-"nonlocal" @keyword
-"pass" @keyword
-"print" @keyword
-"raise" @keyword
-"return" @keyword
-"try" @keyword
-"while" @repeat
-"with" @keyword
-"yield" @keyword
-
-; Additions for nvim-treesitter
-"(" @punctuation.bracket
-")" @punctuation.bracket
-"[" @punctuation.bracket
-"]" @punctuation.bracket
-
-"," @punctuation.delimiter
-"." @punctuation.delimiter
-":" @punctuation.delimiter
+; Class definitions
(class_definition
name: (identifier) @type)
(class_definition
- superclasses: (argument_list
+ superclasses: (argument_list
(identifier) @type))
-(attribute
+((attribute
attribute: (identifier) @field)
-
-((attribute
- attribute: (identifier) @constant)
- (match? @constant "^[A-Z][A-Z_]*$"))
-
-((attribute
- attribute: (identifier) @type)
- (match? @type "^[A-Z][a-z_]+"))
-
-((attribute
- object: (identifier) @type)
- (match? @type "^[A-Z][a-z_]+"))
-
-(class_definition
- body: (block
- (expression_statement
- (assignment
- left: (expression_list
- (identifier) @field)))))
+ (match? @field "^([A-Z])@!.*$"))
((class_definition
body: (block
(expression_statement
(assignment
left: (expression_list
- (identifier) @constant)))))
- (match? @constant "^[A-Z][A-Z_]*$"))
+ (identifier) @field)))))
+ (match? @field "^([A-Z])@!.*$"))
;; Error
(ERROR) @error
diff --git a/queries/python/locals.scm b/queries/python/locals.scm
index 735271663..cdf4d4117 100644
--- a/queries/python/locals.scm
+++ b/queries/python/locals.scm
@@ -30,12 +30,8 @@
; Function defines function and scope
(function_definition
- name: (identifier) @definition.function) @scope
-
-;; Should be extended to when syntax supported
-;(function_definition
- ;name: (identifier) @definition.function
- ;body: (block (expression_statement (string) @definition.function.doc)?)) @scope
+ name: (identifier) @definition.function
+ body: (block (expression_statement (string) @definition.doc)?)) @scope
(class_definition
diff --git a/queries/ruby/highlights.scm b/queries/ruby/highlights.scm
index 9e5852cdf..d7d880043 100644
--- a/queries/ruby/highlights.scm
+++ b/queries/ruby/highlights.scm
@@ -1,32 +1,41 @@
; Keywords
-"alias" @keyword
-"and" @keyword
-"begin" @keyword
-"break" @keyword
-"case" @conditional
-"class" @keyword
-"def" @keyword
-"do" @keyword
-"else" @conditional
-"elsif" @conditional
-"end" @keyword
-"ensure" @keyword
-"for" @repeat
-"if" @conditional
-"in" @keyword
-"module" @keyword
-"next" @keyword
-"or" @keyword
-"rescue" @keyword
-"retry" @keyword
-"return" @keyword
-"then" @keyword
-"unless" @conditional
-"until" @repeat
-"when" @conditional
-"while" @repeat
-"yield" @keyword
+[
+ "alias"
+ "and"
+ "begin"
+ "break"
+ "class"
+ "def"
+ "do"
+ "end"
+ "ensure"
+ "in"
+ "module"
+ "next"
+ "or"
+ "rescue"
+ "retry"
+ "return"
+ "then"
+ "yield"
+ ] @keyword
+
+[
+ "case"
+ "else"
+ "elsif"
+ "if"
+ "unless"
+ "when"
+ ] @conditional
+
+[
+ "for"
+ "until"
+ "while"
+ ] @repeat
+
((identifier) @keyword
(#match? @keyword "^(private|protected|public)$"))
@@ -39,32 +48,45 @@
"defined?" @function
(call
- receiver: (constant) @constant)
+ [
+ receiver: (constant) @constant
+ method: [
+ (identifier)
+ (constant)
+ ] @function
+ ])
+
(method_call
- receiver: (constant) @constant)
-(call
- method: (identifier) @function)
-(method_call
- method: (identifier) @function)
-(call
- method: (constant) @function)
-(method_call
- method: (constant) @function)
+ [
+ receiver: (constant) @constant
+ method: [
+ (identifier)
+ (constant)
+ ] @function
+ ])
; Function definitions
(alias (identifier) @function)
(setter (identifier) @function)
-(method name: (identifier) @function)
-(method name: (constant) @constant)
+
+(method name: [
+ (identifier) @function
+ (constant) @constant
+ ])
+
+(singleton_method name: [
+ (identifier) @function
+ (constant) @constant
+ ])
+
(class name: (constant) @constant)
-(singleton_method name: (identifier) @function)
-(singleton_method name: (constant) @constant)
; Identifiers
-
-(class_variable) @label
-(instance_variable) @label
+[
+ (class_variable)
+ (instance_variable)
+ ] @label
((identifier) @constant.builtin
(#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$"))
@@ -74,8 +96,10 @@
(constant) @constant
-(self) @constant.builtin
-(super) @constant.builtin
+[
+ (self)
+ (super)
+ ] @constant.builtin
(method_parameters (identifier) @parameter)
(lambda_parameters (identifier) @parameter)
@@ -93,22 +117,31 @@
; Literals
-(string) @string
-(bare_string) @string
-(bare_symbol) @constant
-(subshell) @string
-(heredoc_beginning) @constant
-(heredoc_body) @string
-(heredoc_end) @constant
-(symbol) @constant
+[
+ (string)
+ (bare_string)
+ (subshell)
+ (heredoc_body)
+ ] @string
+
+[
+ (bare_symbol)
+ (heredoc_beginning)
+ (heredoc_end)
+ (symbol)
+ ] @constant
+
+(pair key: (symbol) ":" @constant)
(regex) @string.regex
(escape_sequence) @string.escape
(integer) @number
(float) @float
-(nil) @boolean
-(true) @boolean
-(false) @boolean
+[
+ (nil)
+ (true)
+ (false)
+ ] @boolean
(interpolation
"#{" @punctuation.bracket
@@ -118,25 +151,31 @@
; Operators
-"=" @operator
-"=>" @operator
-"->" @operator
-"+" @operator
-"-" @operator
-"*" @operator
-"/" @operator
+[
+ "="
+ "=>"
+ "->"
+ "+"
+ "-"
+ "*"
+ "/"
+ ] @operator
-"," @punctuation.delimiter
-";" @punctuation.delimiter
-"." @punctuation.delimiter
+[
+ ","
+ ";"
+ "."
+ ] @punctuation.delimiter
-"(" @punctuation.bracket
-")" @punctuation.bracket
-"[" @punctuation.bracket
-"]" @punctuation.bracket
-"{" @punctuation.bracket
-"}" @punctuation.bracket
-"%w(" @punctuation.bracket
-"%i(" @punctuation.bracket
+[
+ "("
+ ")"
+ "["
+ "]"
+ "{"
+ "}"
+ "%w("
+ "%i("
+ ] @punctuation.bracket
(ERROR) @error
diff --git a/queries/ruby/locals.scm b/queries/ruby/locals.scm
index f50c2cc99..fe5122d2d 100644
--- a/queries/ruby/locals.scm
+++ b/queries/ruby/locals.scm
@@ -24,8 +24,10 @@
((method) @scope
(set! scope-inherits false))
-(block) @scope
-(do_block) @scope
+[
+ (block)
+ (do_block)
+ ] @scope
(method_parameters (identifier) @definition.function)
(lambda_parameters (identifier) @definition.function)
diff --git a/queries/typescript/highlights.scm b/queries/typescript/highlights.scm
new file mode 100644
index 000000000..dd0dd778f
--- /dev/null
+++ b/queries/typescript/highlights.scm
@@ -0,0 +1,27 @@
+[
+"abstract"
+"declare"
+"enum"
+"export"
+"implements"
+"interface"
+"keyof"
+"namespace"
+"private"
+"protected"
+"public"
+"type"
+] @keyword
+
+(readonly) @keyword
+(type_identifier) @type
+(predefined_type) @type.builtin
+
+(type_arguments
+ "<" @punctuation.bracket
+ ">" @punctuation.bracket)
+
+; Variables
+
+(required_parameter (identifier) @variable.parameter)
+(optional_parameter (identifier) @variable.parameter)
diff --git a/queries/typescript/locals.scm b/queries/typescript/locals.scm
new file mode 100644
index 000000000..cb064b823
--- /dev/null
+++ b/queries/typescript/locals.scm
@@ -0,0 +1,2 @@
+(required_parameter (identifier) @definition)
+(optional_parameter (identifier) @definition)
diff --git a/scripts/pre-push b/scripts/pre-push
new file mode 100755
index 000000000..00d4c5071
--- /dev/null
+++ b/scripts/pre-push
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+# Can be used as a pre-push hook
+# Just symlink this file to .git/hooks/pre-push
+
+echo "Running style check..."
+./scripts/style-check.sh
diff --git a/scripts/style-check.sh b/scripts/style-check.sh
new file mode 100755
index 000000000..181ab4581
--- /dev/null
+++ b/scripts/style-check.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+luacheck `find -name "*.lua"` --codes