Merge branch 'master' into rust-queries

This commit is contained in:
Thomas Vigouroux 2020-07-07 13:28:24 +02:00 committed by GitHub
commit 8a76b16814
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 2887 additions and 1109 deletions

View file

@ -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`**
<details>
<code>
Paste the output here
```
</code>
</details>
**Output of `nvim --version`**
```

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
doc/tags
.luacheckcache

16
.luacheckrc Normal file
View file

@ -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",
}

11
.travis.yml Normal file
View file

@ -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

View file

@ -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
```

201
LICENSE Normal file
View file

@ -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.

View file

@ -1,7 +1,11 @@
[![Gitter](https://badges.gitter.im/nvim-treesitter/community.svg)](https://gitter.im/nvim-treesitter/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.com/nvim-treesitter/nvim-treesitter.svg?branch=master)](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 `<vim-config-dir>/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.

View file

@ -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:

View file

@ -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*

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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()<CR>"
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()<CR>", 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()<CR>", funcname)
api.nvim_buf_set_keymap(buf, mode, mapping, cmd, { silent = true })
end
end

View file

@ -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'
}
}
}

View file

@ -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"
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <buffer=%d> lua require'nvim-treesitter.refactor.highlight_definitions'.highlight_usages(%d)]], bufnr, bufnr))
cmd(string.format([[autocmd CursorMoved <buffer=%d> lua require'nvim-treesitter.refactor.highlight_definitions'.clear_usage_highlights(%d)]], bufnr, bufnr))
cmd(string.format([[autocmd InsertEnter <buffer=%d> 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

View file

@ -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)<CR>]], 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

View file

@ -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)<CR>]], 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

110
queries/bash/highlights.scm Normal file
View file

@ -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)

View file

@ -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

View file

@ -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

103
queries/cpp/highlights.scm Normal file
View file

@ -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

51
queries/cpp/locals.scm Normal file
View file

@ -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)

View file

@ -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

View file

@ -8,8 +8,9 @@
"=" @operator
"<" @punctuation.bracket
">" @punctuation.bracket
"</" @punctuation.bracket
"/>" @punctuation.bracket
[
"<"
">"
"</"
"/>"
] @punctuation.bracket

214
queries/java/highlights.scm Normal file
View file

@ -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

10
queries/java/locals.scm Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -12,6 +12,8 @@
(parameters (identifier) @definition.var)))
((function
(parameters (identifier) @definition.var)))
((function_definition
(parameters (identifier) @definition.var)))
;; Loops
((loop_expression

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -0,0 +1,2 @@
(required_parameter (identifier) @definition)
(optional_parameter (identifier) @definition)

7
scripts/pre-push Executable file
View file

@ -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

3
scripts/style-check.sh Executable file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
luacheck `find -name "*.lua"` --codes