aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTravonteD <tman1300@aol.com>2020-05-11 11:29:40 -0400
committerGitHub <noreply@github.com>2020-05-11 11:29:40 -0400
commit73ea03fb8d45378bb4c86cd98371232403a7495a (patch)
tree67920137c386b0876a57d7bc08ba4a9743f8881d
parentMerge pull request #21 from haorenW1025/master (diff)
parentMerge pull request #47 from theHamsta/fix-typo-contributing.md (diff)
downloadnvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar.gz
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar.bz2
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar.lz
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar.xz
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.tar.zst
nvim-treesitter-73ea03fb8d45378bb4c86cd98371232403a7495a.zip
Merge pull request #1 from nvim-treesitter/master
Updates from master
-rw-r--r--CONTRIBUTING.md99
-rw-r--r--README.md96
-rw-r--r--autoload/nvim_treesitter.vim3
-rw-r--r--doc/nvim-treesitter.txt112
-rw-r--r--doc/tags13
-rw-r--r--lua/nvim-treesitter.lua58
-rw-r--r--lua/nvim-treesitter/configs.lua415
-rw-r--r--lua/nvim-treesitter/health.lua31
-rw-r--r--lua/nvim-treesitter/highlight.lua56
-rw-r--r--lua/nvim-treesitter/incremental_selection.lua78
-rw-r--r--lua/nvim-treesitter/info.lua96
-rw-r--r--lua/nvim-treesitter/install.lua72
-rw-r--r--lua/nvim-treesitter/locals.lua26
-rw-r--r--lua/nvim-treesitter/node_movement.lua91
-rw-r--r--lua/nvim-treesitter/parsers.lua3
-rw-r--r--lua/nvim-treesitter/utils.lua89
-rw-r--r--plugin/nvim-treesitter.vim19
-rw-r--r--queries/lua/highlights.scm25
-rw-r--r--queries/lua/locals.scm24
-rw-r--r--queries/ruby/highlights.scm140
-rw-r--r--queries/ruby/locals.scm45
21 files changed, 1421 insertions, 170 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..46c7a9419
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,99 @@
+# Contributing to `nvim-treesitter`
+
+First of all, thank you very much for contributing to `nvim-treesitter`.
+
+If you haven't already, you should really come and reach out to us on our [gitter](https://gitter.im/nvim-treesitter/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
+room, so we can help you with any question you might have!
+
+As you know, `nvim-treesitter` is roughly splitted in two parts :
+ - Parser configurations : for various things like `locals`, `highlights`
+ - What we like to call *modules* : tiny lua modules that provide a given feature, based on parser configurations
+
+Depending on which part of the plugin you want to contribute to, please read the appropriate section.
+
+## Parser configurations
+
+Contributing to parser configurations is basically modifying one of the `queries/*/*.scm`.
+Each of these `scheme` files contains a *tree-sitter query* for a given purpose.
+Before going any further, we highly suggest that you [read more about tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries).
+
+Each query has an appropriate name, which is then used by modules to extract data from the syntax tree.
+For now two types of queries are used by `nvim-treesitter`:
+ - `highlights.scm` : used for syntax highlighting, using the `highlight` module.
+ - `locals.scm` : used to extract keyword definitions, scopes, references,... using the `locals` module.
+
+For both of these types there is a *norm* you will have to follow so that features work fine.
+Here are some global advices :
+ - If your language is listed [here](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries),
+ you can debug and experiment with your queries there.
+ - 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.
+
+### Highlights
+
+As languages differ quite a lot, here is a set of captures available to you when building a `highlights.scm` query.
+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 `{}`
+
+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`
+
+#### Constants
+`@constant`
+ `builtin`
+ `macro`
+`@string`
+ `regex`
+ `escape`
+`@character`
+`@number`
+`@boolean`
+`@float`
+
+#### Functions
+`@function`
+ `builtin`
+ `macro`
+`@parameter`
+
+`@method`
+`@field` or `@property`
+
+`@constructor`
+
+#### Keywords
+`@conditional`
+`@repeat`
+`@label` for C/Lua-like labels
+`@operator`
+`@keyword`
+`@exception`
+
+`@type`
+ `builtin`
+`@structure`
+
+### Locals
+
+`@definition` for various definitions
+ `function`
+ `method`
+ `var`
+ `macro`
+ `type`
+
+`@scope`
+
+`@reference`
+
diff --git a/README.md b/README.md
index 7a677f2bc..afedbc400 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Treesitter configurations and abstraction layer for Neovim.
## Installation
-You can install `nvim-treesitter` with your favorite package manager, or using the default `pack` feature of neovim !
+You can install `nvim-treesitter` with your favorite package manager, or using the default `pack` feature of Neovim!
### Using a package manager
@@ -79,19 +79,101 @@ bash [✗] not installed
tsx [✗] not installed
```
-And now you should be able to use every functionnality `nvim-treesitter` provides !
+And now you should be able to use every functionality `nvim-treesitter` provides!
+
+## Setup
+
+in your `init.vim`:
+
+```lua
+lua <<EOF
+require'nvim-treesitter.configs'.setup {
+ highlight = {
+ enable = true, -- false will disable the whole extension
+ disable = { 'c', 'rust' }, -- list of language that will be disabled
+ },
+ incremental_selection = { -- this enables incremental selection
+ enable = true,
+ disable = { 'cpp', 'lua' },
+ keymaps = { -- mappings for incremental selection (visual mappings)
+ node_incremental = "<leader>e", -- "grn" by default,
+ scope_incremental = "<leader>f" -- "grc" by default
+ }
+ },
+ node_movement = { -- this enables cursor movement in node hierarchy
+ enable = true,
+ disable = { 'cpp', 'rust' },
+ keymaps = { -- mappings for node movement (normal mappings)
+ move_up = "<a-k>", -- default is to move with alt key hold
+ move_down = "<a-j>",
+ move_left = "<a-h>",
+ move_right = "<a-l>",
+ }
+ },
+ ensure_installed = 'all' -- one of 'all', 'language', or a list of languages
+}
+EOF
+```
+
+## Commands
+
+Each feature can be enabled or disabled by different means:
+```vim
+:TSBufEnable {module} " enable module on current buffer
+:TSBufDisable {module} " disable module on current buffer
+:TSEnableAll {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype.
+:TSDisableAll {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype.
+:TSModuleInfo [{module}] " list information about modules state for each filetype
+```
## Features and Roadmap
-The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for treesitter in neovim,
-but also to add some functionnalities to it:
+The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for Treesitter in Neovim,
+but also to add some functionalities to it:
Some of these features are :
- - [ ] Incremental selection
+ - [x] Incremental selection
- [ ] Syntax based code folding
- - [ ] Consistent syntax highlighting
+ - [x] Consistent syntax highlighting (the api is not quite stable yet)
+ - [x] Cursor movement in node hierachy
+ - [x] Statusline indicator (`require'nvim-treesitter'.statusline(size)`)
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 apreciated !
+The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!
+
+## Supported Languages
+
+For treesitter to work, we need to use query files such as those you can find in `queries/{lang}/{locals,highlights}.scm`
+
+We are looking for maintainers to write query files for their languages.
+
+List of currently supported languages:
+- [x] lua (maintained by @vigoux)
+- [x] ruby (maintained by @TravonteD)
+- [ ] c
+- [ ] go
+- [ ] cpp
+- [ ] rust
+- [ ] python
+- [ ] javascript
+- [ ] typescript
+- [ ] tsx
+- [ ] json
+- [ ] html
+- [ ] csharp
+- [ ] swift
+- [ ] java
+- [ ] ocaml
+- [ ] css
+- [ ] julia
+- [ ] php
+- [ ] bash
+- [ ] scala
+- [ ] haskell
+- [ ] toml
+- [ ] vue
+- [ ] elm
+- [ ] yaml
+- [ ] nix
## Troubleshooting
Before doing anything run `:checkhealth nvim_treesitter`. This will help you find where the bug might come from.
diff --git a/autoload/nvim_treesitter.vim b/autoload/nvim_treesitter.vim
new file mode 100644
index 000000000..715befe0f
--- /dev/null
+++ b/autoload/nvim_treesitter.vim
@@ -0,0 +1,3 @@
+function! nvim_treesitter#statusline(len)
+ return luaeval("require'nvim-treesitter'.statusline(_A)", a:len)
+endfunction
diff --git a/doc/nvim-treesitter.txt b/doc/nvim-treesitter.txt
new file mode 100644
index 000000000..084936bad
--- /dev/null
+++ b/doc/nvim-treesitter.txt
@@ -0,0 +1,112 @@
+*nvim-treesitter*
+
+Minimum version of neovim: nightly
+
+Authors: Yazdani Kiyan <yazdani.kiyan@protonmail.com>, Vigouroux Thomas <tomvig38@gmail.com>
+
+==============================================================================
+INTRODUCTION *nvim-treesitter-intro*
+
+nvim-treesitter wraps the neovim treesitter api to provide functionnalities such
+as highlighting and incremental selection, and a command to easily install parsers.
+
+==============================================================================
+QUICK START *nvim-treesitter-quickstart*
+
+Install the parser for your language
+
+>
+ :TSInstall {language}
+<
+
+To get a list of supported languages
+
+>
+ :TSInstallInfo
+<
+
+By default, everything is disabled. To enable support for features, in your `init.vim`:
+
+>
+ lua <<EOF
+ require'nvim-treesitter.configs'.setup {
+ highlight = {
+ enable = true, -- false will disable the whole extension
+ disable = { 'c', 'rust' }, -- list of language that will be disabled
+ },
+ incremental_selection = { -- this enables incremental selection
+ enable = true,
+ disable = { 'cpp', 'lua' },
+ keymaps = { -- mappings for incremental selection (visual mappings)
+ node_incremental = "<leader>e", -- "grn" by default,
+ scope_incremental = "<leader>f" -- "grc" by default
+ }
+ },
+ node_movement = { -- this cursor movement in node hierachy
+ enable = true,
+ disable = { 'cpp', 'rust' },
+ keymaps = { -- mappings for node movement (normal mappings)
+ move_up = "<a-k>", -- default is to move with alt key hold
+ move_down = "<a-j>",
+ move_left = "<a-h>",
+ move_right = "<a-l>",
+ }
+ },
+ ensure_installed = 'all' -- one of 'all', 'language', or a list of languages
+ }
+<
+
+==============================================================================
+COMMANDS *nvim-treesitter-commands*
+
+|:TSInstall| {language} *:TSInstall*
+
+Download, compile and install a parser for {language}
+
+|:TSInstallInfo| *:TSInstallInfo*
+
+List informations about currently installed parsers
+
+|:TSBufEnable| {module} *:TSBufEnable*
+
+Enable {module} on the current buffer.
+A list of modules can be found at |:TSModuleInfo|
+
+|:TSBufDisable| {module} *:TSBufDisable*
+
+Disable {module} on the current buffer
+A list of modules can be found at |:TSModuleInfo|
+
+|:TSBufEnableAll| {module} [{language}] *:TSBufEnableAll*
+
+Enable {module} for the session
+if {language} is specified, enable module for the session only for this
+particular language.
+A list of modules can be found at |:TSModuleInfo|
+A list of languages can be found at |:TSInstallInfo|
+
+|:TSBufDisableAll| {module} [{language}] *:TSBufDisableAll*
+
+Disable {module} for the session
+if {language} is specified, disable module for the session only for this
+particular language.
+A list of modules can be found at |:TSModuleInfo|
+A list of languages can be found at |:TSInstallInfo|
+
+|:TSModuleInfo| [{module}] *:TSModuleInfo*
+
+List modules state for the current session.
+
+==============================================================================
+FUNCTIONS~
+ *nvim-treesitter-functions*
+
+|nvim_treesitter#statusline(size)|
+ *nvim_treesitter#statusline()*
+
+Returns a string describing the current position in the syntax tree. This
+could be used as a statusline indicator.
+Note: The `size` argument is optionnal. When specified, the string will not be
+ longer than `size`.
+
+ vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/doc/tags b/doc/tags
new file mode 100644
index 000000000..f712ecf00
--- /dev/null
+++ b/doc/tags
@@ -0,0 +1,13 @@
+: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-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#statusline() nvim-treesitter.txt /*nvim_treesitter#statusline()*
diff --git a/lua/nvim-treesitter.lua b/lua/nvim-treesitter.lua
index b053f39b0..90b5468a3 100644
--- a/lua/nvim-treesitter.lua
+++ b/lua/nvim-treesitter.lua
@@ -1,30 +1,58 @@
local api = vim.api
-local parsers = require'nvim-treesitter.parsers'
-local configs = require 'nvim-treesitter.configs'
local install = require'nvim-treesitter.install'
local locals = require'nvim-treesitter.locals'
-local highlight = require'nvim-treesitter.highlight'
+local utils = require'nvim-treesitter.utils'
+local info = require'nvim-treesitter.info'
+local configs = require'nvim-treesitter.configs'
local M = {}
-function M.available_parsers()
- return vim.tbl_keys(configs.repositories)
-end
-
-- This function sets up everythin needed for a given language
-- this is the main interface through the plugin
function M.setup(lang)
- if parsers.has_parser(lang) then
- local autocmd = "autocmd NvimTreesitter FileType %s lua require'nvim-treesitter.highlight'.setup()"
- api.nvim_command(string.format(autocmd, lang))
+ utils.setup_commands('install', install.commands)
+ utils.setup_commands('info', info.commands)
+ utils.setup_commands('configs', configs.commands)
+
+ for _, ft in pairs(configs.available_parsers()) do
+ for _, mod in pairs(configs.available_modules()) do
+ if configs.is_enabled(mod, ft) then
+ local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
+ api.nvim_command(string.format("autocmd FileType %s %s", ft, cmd))
+ end
+ end
end
end
--- This function initialize the plugin
--- it is run at startup
-M._root = {}
-function M._root.setup()
- install.setup()
+function M.statusline(indicator_size)
+ local indicator_size = indicator_size or 1000
+ local expr = require"nvim-treesitter.utils".expression_at_point()
+ local current_node =
+ require'nvim-treesitter.node_movement'.current_node[api.nvim_get_current_buf()]
+
+ local indicator = ""
+ while expr and (#indicator + #(expr:type()) + 5) < indicator_size do
+
+ local prefix = ""
+ if expr:parent() then
+ prefix = "->"
+ end
+
+
+ if expr == current_node then
+ indicator = string.format("%s[%s]%s", prefix, expr:type(), indicator)
+ else
+ indicator = prefix .. expr:type() .. indicator
+ end
+
+ expr = expr:parent()
+ end
+
+ if expr then
+ return "..." .. indicator
+ else
+ return indicator
+ end
end
return M
diff --git a/lua/nvim-treesitter/configs.lua b/lua/nvim-treesitter/configs.lua
index fd3b41d29..9695da5af 100644
--- a/lua/nvim-treesitter/configs.lua
+++ b/lua/nvim-treesitter/configs.lua
@@ -1,88 +1,421 @@
-local M = {}
+local api = vim.api
+local queries = require'nvim-treesitter.query'
+local parser_utils = require'nvim-treesitter.parsers'
+local parsers = {}
-M.repositories = {
- javascript = {
+parsers.javascript = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-javascript",
files = { "src/parser.c", "src/scanner.c" },
- },
- c = {
+ }
+}
+
+parsers.c = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-c",
files = { "src/parser.c" }
- },
- cpp = {
+ }
+}
+
+parsers.cpp = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-cpp",
files = { "src/parser.c", "src/scanner.cc" }
- },
- rust = {
+ }
+}
+
+parsers.rust = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-rust",
files = { "src/parser.c", "src/scanner.c" },
- },
- lua = {
+ }
+}
+
+parsers.lua = {
+ install_info = {
url = "https://github.com/nvim-treesitter/tree-sitter-lua",
files = { "src/parser.c", "src/scanner.cc" }
- },
- python = {
+ }
+}
+
+parsers.python = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-python",
files = { "src/parser.c", "src/scanner.cc" },
- },
- go = {
+ }
+}
+
+parsers.go = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-go",
files = { "src/parser.c" },
- },
- ruby = {
+ }
+}
+
+parsers.ruby = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-ruby",
files = { "src/parser.c", "src/scanner.cc" },
- },
- bash = {
+ }
+}
+
+parsers.bash = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-bash",
files = { "src/parser.c", "src/scanner.cc" },
- },
- php = {
+ }
+}
+
+parsers.php = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-php",
files = { "src/parser.c", "src/scanner.cc" },
- },
- java = {
+ }
+}
+
+parsers.java = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-java",
files = { "src/parser.c" },
- },
- html = {
+ }
+}
+
+parsers.html = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-html",
files = { "src/parser.c", "src/scanner.cc" },
- },
- julia = {
+ }
+}
+
+parsers.julia = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-julia",
files = { "src/parser.c", "src/scanner.c" },
- },
- json = {
+ }
+}
+
+parsers.json = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-json",
files = { "src/parser.c" },
- },
- css = {
+ }
+}
+
+parsers.css = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-css",
files = { "src/parser.c", "src/scanner.c" },
- },
- ocaml = {
+ }
+}
+
+parsers.ocaml = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-ocaml",
files = { "src/parser.c", "src/scanner.cc" },
- },
- swift = {
+ }
+}
+
+parsers.swift = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-swift",
files = { "src/parser.c" },
- },
- csharp = {
+ }
+}
+
+parsers.csharp = {
+ install_info = {
url = "https://github.com/tree-sitter/tree-sitter-c-sharp",
files = { "src/parser.c", "src/scanner.c" },
- },
- typescript = {
+ }
+}
+
+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"
- },
- tsx = {
+ }
+}
+
+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" },
+ }
+}
+
+-- @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
+-- @is_supported function which, given a ft, will return true if the ft works on the module
+local config = {
+ modules = {
+ highlight = {
+ enable = false,
+ disable = {},
+ is_supported = function(ft)
+ return queries.get_query(ft, 'highlights') ~= nil
+ end
+ },
+ incremental_selection = {
+ enable = false,
+ disable = {},
+ keymaps = {
+ node_incremental="grn",
+ scope_incremental="grc"
+ },
+ is_supported = function() return true end
+ },
+ node_movement = {
+ enable = false,
+ disable = {},
+ is_supported = function() return true end,
+ keymaps = {
+ move_up = "<a-k>",
+ move_down = "<a-j>",
+ move_left = "<a-h>",
+ move_right = "<a-l>",
+ },
+ },
+ -- folding = {
+ -- enable = false,
+ -- disable = {},
+ -- keymaps = {},
+ -- is_supported = function() return false end
+ -- }
+ },
+ ensure_installed = nil
+}
+
+local M = {}
+
+local function enable_module(mod, bufnr, ft)
+ 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
+ return
+ end
+
+ local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
+ loaded_mod.attach(bufnr, ft)
+end
+
+local function enable_mod_conf_autocmd(mod, ft)
+ if not config.modules[mod] or M.is_enabled(mod, ft) 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)
+ break
+ end
+ end
+end
+
+local function enable_all(mod, ft)
+ if not config.modules[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)
+ end
+ end
+ if ft then
+ if parser_utils.has_parser(ft) then
+ enable_mod_conf_autocmd(mod, ft)
+ end
+ else
+ for _, ft in pairs(M.available_parsers()) do
+ if parser_utils.has_parser(ft) then
+ enable_mod_conf_autocmd(mod, ft)
+ end
+ end
+ end
+ config.modules[mod].enable = true
+end
+
+local function disable_module(mod, bufnr, ft)
+ 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
+ return
+ end
+
+ local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
+ loaded_mod.detach(bufnr, ft)
+end
+
+local function disable_mod_conf_autocmd(mod, ft)
+ if not config.modules[mod] or not M.is_enabled(mod, ft) then return end
+
+ api.nvim_command(string.format("autocmd! FileType %s", ft))
+ table.insert(config.modules[mod].disable, ft)
+end
+
+local function disable_all(mod, ft)
+ 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)
+ end
+ end
+ if ft then
+ disable_mod_conf_autocmd(mod, ft)
+ else
+ for _, ft in pairs(M.available_parsers()) do
+ disable_mod_conf_autocmd(mod, ft)
+ end
+ config.modules[mod].enable = false
+ end
+end
+
+M.commands = {
+ TSBufEnable = {
+ run = enable_module,
+ 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 parser_utils.has_parser(ft) then
+ return false
+ end
+
+ local module_config = config.modules[mod]
+ if not module_config then return false end
+
+ if not module_config.enable or not module_config.is_supported(ft) then
+ return false
+ end
+
+ for _, parser in pairs(module_config.disable) do
+ if ft == parser then return false end
+ end
+ return true
+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
+ config.modules[mod].keymaps = data.keymaps
+ end
+ elseif mod == 'ensure_installed' then
+ config.ensure_installed = data
+ require'nvim-treesitter/install'.ensure_installed(data)
+ 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)
+end
+
+function M.get_module(mod)
+ return config.modules[mod]
+end
+
return M
diff --git a/lua/nvim-treesitter/health.lua b/lua/nvim-treesitter/health.lua
index 25da7fe35..7ba1cae65 100644
--- a/lua/nvim-treesitter/health.lua
+++ b/lua/nvim-treesitter/health.lua
@@ -3,6 +3,7 @@ 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 health_start = vim.fn["health#report_start"]
@@ -13,7 +14,7 @@ local health_error = vim.fn['health#report_error']
local M = {}
-local function configs_health()
+local function install_health()
if fn.executable('git') == 0 then
health_error('`git` executable not found.', {
'Install it with your package manager.',
@@ -34,15 +35,36 @@ local function configs_health()
end
end
+local function highlight_health(lang)
+ if not queries.get_query(lang, "highlights") then
+ health_warn("No `highlights.scm` query found for " .. lang, {
+ "Open an issue at https://github.com/nvim-treesitter/nvim-treesitter"
+ })
+ else
+ health_ok("`highlights.scm` found.")
+ end
+end
+
+function locals_health(lang)
+ if not queries.get_query(lang, "locals") then
+ health_warn("No `locals.scm` query found for " .. lang, {
+ "Open an issue at https://github.com/nvim-treesitter/nvim-treesitter"
+ })
+ else
+ health_ok("`locals.scm` found.")
+ end
+end
+
+
-- TODO(vigoux): Maybe we should move each check to be perform in its own module
function M.checkhealth()
-- Installation dependency checks
health_start('Installation')
- configs_health()
+ install_health()
local missing_parsers = {}
-- Parser installation checks
- for parser_name in pairs(configs.repositories) do
+ for _, parser_name in pairs(configs.available_parsers()) do
local installed = #api.nvim_get_runtime_file('parser/'..parser_name..'.so', false)
-- Only print informations about installed parsers
@@ -50,7 +72,8 @@ function M.checkhealth()
health_start(parser_name .. " parser healthcheck")
health_ok(parser_name .. " parser found.")
- locals.checkhealth(parser_name)
+ locals_health(parser_name)
+ highlight_health(parser_name)
elseif installed > 1 then
health_warn(string.format("Multiple parsers found for %s, only %s will be used.", parser_name, installed[1]))
else
diff --git a/lua/nvim-treesitter/highlight.lua b/lua/nvim-treesitter/highlight.lua
index 110954a6a..9cf259639 100644
--- a/lua/nvim-treesitter/highlight.lua
+++ b/lua/nvim-treesitter/highlight.lua
@@ -1,12 +1,53 @@
local api = vim.api
-local queries = require'nvim-treesitter.query'
local ts = vim.treesitter
+local queries = require'nvim-treesitter.query'
local M = {
- highlighters={}
+ highlighters = {}
}
-function M.setup(bufnr, ft)
+local hlmap = vim.treesitter.TSHighlighter.hl_map
+
+-- Misc
+hlmap.error = "Error"
+hlmap["punctuation.delimiter"] = "Delimiter"
+hlmap["punctuation.bracket"] = "Delimiter"
+
+-- 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"
+
+-- 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"] = "Special"
+
+-- Keywords
+hlmap["conditional"] = "Conditional"
+hlmap["repeat"] = "Repeat"
+hlmap["label"] = "Label"
+hlmap["operator"] = "Operator"
+hlmap["keyword"] = "Keyword"
+hlmap["exception"] = "Exception"
+
+hlmap["type"] = "Type"
+hlmap["type.builtin"] = "Type"
+hlmap["structure"] = "Structure"
+
+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')
@@ -16,4 +57,13 @@ function M.setup(bufnr, ft)
M.highlighters[buf] = ts.TSHighlighter.new(query, buf, ft)
end
+function M.detach(bufnr)
+ local buf = bufnr or api.nvim_get_current_buf()
+ if M.highlighters[buf] then
+ M.highlighters[buf]:set_query("")
+ M.highlighters[buf] = nil
+ end
+ api.nvim_buf_set_option(buf, 'syntax', 'on')
+end
+
return M
diff --git a/lua/nvim-treesitter/incremental_selection.lua b/lua/nvim-treesitter/incremental_selection.lua
new file mode 100644
index 000000000..70eefe37c
--- /dev/null
+++ b/lua/nvim-treesitter/incremental_selection.lua
@@ -0,0 +1,78 @@
+local api = vim.api
+local utils = require'nvim-treesitter.utils'
+local parsers = require'nvim-treesitter.parsers'
+local M = {}
+
+local function node_range_to_vim(node)
+ if not node then return end
+
+ local start_row, start_col, end_row, end_col = node:range()
+
+ local select_range = [[
+ call cursor(%d, %d)
+ normal v
+ call cursor(%d, %d)
+ ]]
+ local exec_command = string.format(select_range,
+ start_row+1, start_col+1,
+ end_row+1, end_col+1)
+
+ api.nvim_exec(exec_command, false)
+end
+
+local function select_incremental(increment_func)
+ return function()
+ local buf, sel_start_line, sel_start_col, _ = unpack(vim.fn.getpos("'<"))
+ local buf, sel_end_line, sel_end_col, _ = unpack(vim.fn.getpos("'>"))
+
+ local node = nil
+ if parsers.has_parser() then
+ local root = parsers.get_parser():parse():root()
+ node = root:named_descendant_for_range(sel_start_line-1, sel_start_col-1, sel_end_line-1, sel_end_col)
+ local node_start_row, node_start_col, node_end_row, node_end_col = node:range()
+
+ if (sel_start_line-1) == node_start_row and (sel_start_col-1) == node_start_col
+ and (sel_end_line-1) == node_end_row and sel_end_col == node_end_col then
+ node = increment_func(node)
+ end
+ end
+
+ return node_range_to_vim(node)
+ end
+end
+
+M.node_incremental = select_incremental(function(node)
+ if node then
+ return node:parent() or node
+ end
+end)
+
+M.scope_incremental = select_incremental(function(node)
+ if node then
+ return utils.smallest_containing_scope(node:parent() or node)
+ end
+end)
+
+function M.attach(bufnr)
+ local buf = bufnr or api.nvim_get_current_buf()
+
+ local config = require'nvim-treesitter.configs'.get_module('incremental_selection')
+ for funcname, mapping in pairs(config.keymaps) do
+ api.nvim_buf_set_keymap(buf, 'v', mapping,
+ string.format(":lua require'nvim-treesitter.incremental_selection'.%s()<CR>", funcname), { silent = true })
+ api.nvim_buf_set_keymap(buf, 'o', mapping,
+ string.format(":normal v%s<CR>", mapping), { silent = true })
+ end
+end
+
+function M.detach(bufnr)
+ local buf = bufnr or api.nvim_get_current_buf()
+
+ local config = require'nvim-treesitter.configs'.get_module('incremental_selection')
+ for _, mapping in pairs(config.keymaps) do
+ api.nvim_buf_del_keymap(buf, 'v', mapping)
+ api.nvim_buf_del_keymap(buf, 'o', mapping)
+ end
+end
+
+return M
diff --git a/lua/nvim-treesitter/info.lua b/lua/nvim-treesitter/info.lua
new file mode 100644
index 000000000..18b1b611f
--- /dev/null
+++ b/lua/nvim-treesitter/info.lua
@@ -0,0 +1,96 @@
+local api = vim.api
+local configs = require'nvim-treesitter.configs'
+
+local M = {}
+
+local function install_info()
+ local max_len = 0
+ for _, ft in pairs(configs.available_parsers()) do
+ if #ft > max_len then max_len = #ft end
+ end
+
+ for _, ft in pairs(configs.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
+ api.nvim_out_write("[✓] installed\n")
+ else
+ api.nvim_out_write("[✗] not installed\n")
+ end
+ end
+end
+
+local function print_info_module(sorted_filetypes, mod)
+ local max_str_len = #sorted_filetypes[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
+ api.nvim_out_write('✓')
+ else
+ api.nvim_out_write('✗')
+ end
+ api.nvim_out_write('\n')
+ end
+end
+
+local function print_info_modules(sorted_filetypes)
+ local max_str_len = #sorted_filetypes[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 _, 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
+ api.nvim_out_write('✓')
+ else
+ api.nvim_out_write('✗')
+ end
+ api.nvim_out_write(string.rep(' ', pad_len - 1))
+ end
+ api.nvim_out_write('\n')
+ end
+end
+
+local function module_info(mod)
+ if mod and not configs.get_config()[mod] then return end
+
+ local ft_by_len = configs.available_parsers()
+ table.sort(ft_by_len, function(a, b) return #a > #b end)
+ if mod then
+ print_info_module(ft_by_len, mod)
+ else
+ print_info_modules(ft_by_len)
+ end
+end
+
+M.commands = {
+ TSInstallInfo = {
+ 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'
+ }
+}
+
+return M
diff --git a/lua/nvim-treesitter/install.lua b/lua/nvim-treesitter/install.lua
index b00d3d773..77e0fccc4 100644
--- a/lua/nvim-treesitter/install.lua
+++ b/lua/nvim-treesitter/install.lua
@@ -1,7 +1,9 @@
local api = vim.api
local fn = vim.fn
local luv = vim.loop
-local repositories = require'nvim-treesitter/configs'.repositories
+local configs = require'nvim-treesitter/configs'
+local parsers = configs.get_parser_configs()
+local has_parser = require'nvim-treesitter/parsers'.has_parser
local M = {}
@@ -33,14 +35,16 @@ local function get_cache_dir()
end
local function iter_cmd(cmd_list, i, ft)
- if i == #cmd_list then return print('Treesitter parser for '..ft..' has been installed') end
+ if i == #cmd_list + 1 then return print('Treesitter parser for '..ft..' has been installed') end
- local attr = cmd_list[i + 1]
+ local attr = cmd_list[i]
if attr.info then print(attr.info) end
+ local handle
+
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
+ if code ~= 0 then return api.nvim_err_writeln(attr.err) end
iter_cmd(cmd_list, i + 1, ft)
end))
end
@@ -75,12 +79,12 @@ local function run_install(cache_folder, package_path, ft, repo)
args = vim.tbl_flatten({
'-o',
'parser.so',
+ '-I./src',
+ repo.files,
'-shared',
+ '-Os',
'-lstdc++',
'-fPIC',
- '-Os',
- '-I./src',
- repo.files
}),
cwd = compile_location
}
@@ -99,7 +103,7 @@ local function run_install(cache_folder, package_path, ft, repo)
}
}
- iter_cmd(command_list, 0, ft)
+ iter_cmd(command_list, 1, ft)
end
-- TODO(kyazdani): this should work on windows too
@@ -118,14 +122,15 @@ local function install(ft)
if not string.match(yesno, '^y.*') then return end
end
- local repository = repositories[ft]
- if not repository then
+ local parser_config = parsers[ft]
+ if not parser_config then
return api.nvim_err_writeln('Parser not available for language '..ft)
end
+ local install_info = parser_config.install_info
vim.validate {
- url={ repository.url, 'string' },
- files={ repository.files, 'table' }
+ url={ install_info.url, 'string' },
+ files={ install_info.files, 'table' }
}
if fn.executable('git') == 0 then
@@ -138,26 +143,27 @@ local function install(ft)
local cache_folder, err = get_cache_dir()
if err then return api.nvim_err_writeln(err) end
- run_install(cache_folder, package_path, ft, repository)
+ run_install(cache_folder, package_path, ft, install_info)
end
-local function install_info()
- local max_len = 0
- for parser_name, _ in pairs(repositories) do
- if #parser_name > max_len then max_len = #parser_name end
- end
- for parser_name, _ in pairs(repositories) do
- local is_installed = #api.nvim_get_runtime_file('parser/'..parser_name..'.so', false) > 0
- api.nvim_out_write(parser_name..string.rep(' ', max_len - #parser_name + 1))
- if is_installed then
- api.nvim_out_write("[✓] installed\n")
+M.ensure_installed = function(languages)
+ if type(languages) == 'string' then
+ if languages == 'all' then
+ languages = configs.available_parsers()
else
- api.nvim_out_write("[✗] not installed\n")
+ languages = {languages}
+ end
+ end
+
+ for _, ft in ipairs(languages) do
+ if not has_parser(ft) then
+ install(ft)
end
end
end
+
M.commands = {
TSInstall = {
run = install,
@@ -166,25 +172,7 @@ M.commands = {
"-complete=custom,v:lua.ts_installable_parsers"
},
description = '`:TSInstall {ft}` installs a parser under nvim-treesitter/parser/{name}.so'
- },
- TSInstallInfo = {
- run = install_info,
- args = { "-nargs=0" },
- description = '`:TSInstallInfo` print installation state for every filetype'
}
}
-function M.setup()
- for command_name, def in pairs(M.commands) do
- local call_fn = string.format("lua require'nvim-treesitter.install'.commands.%s.run(<f-args>)", command_name)
- local parts = vim.tbl_flatten({
- "command!",
- def.args,
- command_name,
- call_fn,
- })
- api.nvim_command(table.concat(parts, " "))
- end
-end
-
return M
diff --git a/lua/nvim-treesitter/locals.lua b/lua/nvim-treesitter/locals.lua
index 388c7e489..b16408ac5 100644
--- a/lua/nvim-treesitter/locals.lua
+++ b/lua/nvim-treesitter/locals.lua
@@ -10,22 +10,6 @@ local M = {
locals={}
}
-function M.checkhealth(lang)
- 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']
-
- if not queries.get_query(lang, "locals") then
- health_warn("No `locals.scm` query found for " .. lang, {
- "Open an issue at https://github.com/nvim-treesitter/nvim-treesitter"
- })
- else
- health_ok("`locals.scm` found.")
- end
-end
-
function M.collect_locals(bufnr)
local ft = api.nvim_buf_get_option(bufnr, "ft")
if not ft then return end
@@ -69,7 +53,7 @@ function M.get_definitions(bufnr)
for _, loc in ipairs(locals) do
if loc.definition then
- table.insert(defs, {definition=loc.definition, kind=loc.kind})
+ table.insert(defs, loc.definition)
end
end
@@ -82,8 +66,8 @@ function M.get_scopes(bufnr)
local scopes = {}
for _, loc in ipairs(locals) do
- if loc.scope then
- table.insert(scopes, loc.scope)
+ if loc.scope and loc.scope.node then
+ table.insert(scopes, loc.scope.node)
end
end
@@ -96,8 +80,8 @@ function M.get_references(bufnr)
local refs = {}
for _, loc in ipairs(locals) do
- if loc.reference then
- table.insert(refs, loc.reference)
+ if loc.reference and loc.reference.node then
+ table.insert(refs, loc.reference.node)
end
end
diff --git a/lua/nvim-treesitter/node_movement.lua b/lua/nvim-treesitter/node_movement.lua
new file mode 100644
index 000000000..5d4813bc4
--- /dev/null
+++ b/lua/nvim-treesitter/node_movement.lua
@@ -0,0 +1,91 @@
+local api = vim.api
+local parsers = require'nvim-treesitter.parsers'
+local utils = require'nvim-treesitter.utils'
+local M = {}
+
+
+M.NodeMovementKind = {
+ up = 'up',
+ down = 'down',
+ left = 'left',
+ right = 'right',
+}
+
+M.current_node = {}
+
+local function node_start_to_vim(node)
+ if not node then return end
+
+ local row, col = node:start()
+ local exec_command = string.format('call cursor(%d, %d)', row+1, col+1)
+ api.nvim_exec(exec_command, false)
+end
+
+M.do_node_movement = function(kind)
+ local buf, line, col = unpack(vim.fn.getpos("."))
+
+ local current_node = M.current_node[buf]
+
+ if current_node then
+ local node_line, node_col = current_node:start()
+ if line-1 ~= node_line or col-1 ~= node_col then
+ current_node = nil
+ end
+ end
+ local destination_node
+
+ if parsers.has_parser() then
+ local root = parsers.get_parser():parse():root()
+ if not current_node then
+ current_node = root:named_descendant_for_range(line-1, col-1, line-1, col)
+ end
+
+ if kind == M.NodeMovementKind.up then
+ destination_node = current_node:parent()
+ elseif kind == M.NodeMovementKind.down then
+ if current_node:named_child_count() > 0 then
+ destination_node = current_node:named_child(0)
+ else
+ local next_node = utils.get_next_node(current_node)
+ if next_node and next_node:named_child_count() > 0 then
+ destination_node = next_node:named_child(0)
+ end
+ end
+ elseif kind == M.NodeMovementKind.left then
+ destination_node = utils.get_previous_node(current_node, true, true)
+ elseif kind == M.NodeMovementKind.right then
+ destination_node = utils.get_next_node(current_node, true, true)
+ end
+ M.current_node[buf] = destination_node or current_node
+ end
+
+ if destination_node then
+ node_start_to_vim(destination_node)
+ end
+end
+
+M.move_up = function() M.do_node_movement(M.NodeMovementKind.up) end
+M.move_down = function() M.do_node_movement(M.NodeMovementKind.down) end
+M.move_left = function() M.do_node_movement(M.NodeMovementKind.left) end
+M.move_right = function() M.do_node_movement(M.NodeMovementKind.right) end
+
+function M.attach(bufnr)
+ local buf = bufnr or api.nvim_get_current_buf()
+
+ local config = require'nvim-treesitter.configs'.get_module('node_movement')
+ for funcname, mapping in pairs(config.keymaps) do
+ api.nvim_buf_set_keymap(buf, 'n', mapping,
+ string.format(":lua require'nvim-treesitter.node_movement'.%s()<CR>", funcname), { silent = true })
+ end
+end
+
+function M.detach(bufnr)
+ local buf = bufnr or api.nvim_get_current_buf()
+
+ local config = require'nvim-treesitter.configs'.get_module('node_movement')
+ for _, mapping in pairs(config.keymaps) do
+ api.nvim_buf_del_keymap(buf, 'n', mapping)
+ end
+end
+
+return M
diff --git a/lua/nvim-treesitter/parsers.lua b/lua/nvim-treesitter/parsers.lua
index e046ca45c..07f3e9d34 100644
--- a/lua/nvim-treesitter/parsers.lua
+++ b/lua/nvim-treesitter/parsers.lua
@@ -5,11 +5,12 @@ local M = {}
function M.has_parser(lang)
local lang = lang or api.nvim_buf_get_option(0, 'filetype')
+ 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)
- if M.has_parser() then
+ 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
diff --git a/lua/nvim-treesitter/utils.lua b/lua/nvim-treesitter/utils.lua
index 8313a46a7..9d591eab6 100644
--- a/lua/nvim-treesitter/utils.lua
+++ b/lua/nvim-treesitter/utils.lua
@@ -1,6 +1,7 @@
-- Utils collection for nvim-treesitter
local api = vim.api
local parsers = require'nvim-treesitter.parsers'
+local locals = require'nvim-treesitter.locals'
local M = {}
@@ -63,4 +64,92 @@ function M.is_parent(dest, source)
return false
end
+function M.setup_commands(mod, commands)
+ for command_name, def in pairs(commands) do
+ local call_fn = string.format("lua require'nvim-treesitter.%s'.commands.%s.run(<f-args>)", mod, command_name)
+ local parts = vim.tbl_flatten({
+ "command!",
+ def.args,
+ command_name,
+ call_fn,
+ })
+ api.nvim_command(table.concat(parts, " "))
+ end
+end
+
+--- Gets the smallest scope which contains @param node
+function M.smallest_containing_scope(node, bufnr)
+ local bufnr = bufnr or api.nvim_get_current_buf()
+
+ local root = parsers.get_parser(bufnr):parse():root()
+ if not node then return root end
+
+ local scopes = locals.get_scopes(bufnr)
+ local current = node
+ while current ~= nil and not vim.tbl_contains(scopes, current) do
+ current = current:parent()
+ end
+
+ return current or root
+end
+
+--- Get next node with same parent
+-- @param node node
+-- @param allow_switch_parents allow switching parents if last node
+-- @param allow_next_parent allow next parent if last node and next parent without children
+function M.get_next_node(node, allow_switch_parents, allow_next_parent)
+ local destination_node
+ local parent = node:parent()
+
+ if parent then
+ local found_pos = 0
+ for i = 0,parent:named_child_count()-1,1 do
+ if parent:named_child(i) == node then
+ found_pos = i
+ break
+ end
+ end
+ if parent:named_child_count() > found_pos + 1 then
+ destination_node = parent:named_child(found_pos + 1)
+ elseif allow_switch_parents then
+ local next_node = M.get_next_node(node:parent())
+ if next_node and next_node:named_child_count() > 0 then
+ destination_node = next_node:named_child(0)
+ elseif next_node and allow_next_parent then
+ destination_node = next_node
+ end
+ end
+ end
+ return destination_node
+end
+
+--- Get previous node with same parent
+-- @param node node
+-- @param allow_switch_parents allow switching parents if first node
+-- @param allow_previous_parent allow previous parent if first node and previous parent without children
+function M.get_previous_node(node, allow_switch_parents, allow_previous_parent)
+ local destination_node
+ local parent = node:parent()
+ if parent then
+ local found_pos = 0
+ for i = 0,parent:named_child_count()-1,1 do
+ if parent:named_child(i) == node then
+ found_pos = i
+ break
+ end
+ end
+ if 0 < found_pos then
+ destination_node = parent:named_child(found_pos - 1)
+ elseif allow_switch_parents then
+ local previous_node = M.get_previous_node(node:parent())
+ if previous_node and previous_node:named_child_count() > 0 then
+ destination_node = previous_node:named_child(previous_node:named_child_count() - 1)
+ elseif previous_node and allow_previous_parent then
+ destination_node = previous_node
+ end
+ end
+ end
+ return destination_node
+end
+
return M
diff --git a/plugin/nvim-treesitter.vim b/plugin/nvim-treesitter.vim
index 06c760a18..30ee3241d 100644
--- a/plugin/nvim-treesitter.vim
+++ b/plugin/nvim-treesitter.vim
@@ -1,17 +1,20 @@
-" Last Change: 2020 avril 19
+" Last Change: 2020 avril 25
if exists('g:loaded_nvim_treesitter')
finish
endif
+augroup NvimTreesitter
+augroup END
+
+let g:loaded_nvim_treesitter = 1
+
lua << EOF
ts_installable_parsers = function()
- return table.concat(require'nvim-treesitter'.available_parsers(), '\n')
+ return table.concat(require'nvim-treesitter.configs'.available_parsers(), '\n')
+end
+ts_available_modules = function()
+ return table.concat(require'nvim-treesitter.configs'.available_modules(), '\n')
end
-require'nvim-treesitter'._root.setup()
+require'nvim-treesitter'.setup()
EOF
-
-let g:loaded_nvim_treesitter = 1
-
-augroup NvimTreesitter
-augroup END
diff --git a/queries/lua/highlights.scm b/queries/lua/highlights.scm
index 3077be108..a674638e8 100644
--- a/queries/lua/highlights.scm
+++ b/queries/lua/highlights.scm
@@ -3,16 +3,16 @@
;;; Builtins
;; Keywords
"local" @keyword
-"if" @keyword
-"then" @keyword
-"else" @keyword
-"elseif" @keyword
+"if" @conditional
+"then" @conditional
+"else" @conditional
+"elseif" @conditional
"end" @keyword
"return" @keyword
-"do" @keyword
-"while" @keyword
-"repeat" @keyword
-"for" @keyword
+"do" @repeat
+"while" @repeat
+"repeat" @repeat
+"for" @repeat
(break_statement) @keyword
"goto" @keyword
@@ -43,15 +43,16 @@
"#" @operator
;; Constants
-(false) @constant
-(true) @constant
-(nil) @constant
+(false) @boolean
+(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 "{" @operator "}" @operator)
+(table "{" @constructor "}" @constructor)
(comment) @comment
(string) @string
(number) @number
diff --git a/queries/lua/locals.scm b/queries/lua/locals.scm
index 5f21e0aaf..ee2927328 100644
--- a/queries/lua/locals.scm
+++ b/queries/lua/locals.scm
@@ -2,25 +2,20 @@
;; Variable and field declarations
((variable_declarator
- (identifier) @definition)
- (set! definition.kind "v"))
+ (identifier) @definition.var))
((variable_declarator
- (field_expression object:(*) @definition.associated (property_identifier) @definition))
- (set! difinition.kind "v"))
+ (field_expression object:(*) @definition.associated (property_identifier) @definition.var)))
;; Parameters
((local_function
- (parameters (identifier) @definition))
- (set! definition.kind "v"))
+ (parameters (identifier) @definition.var)))
((function
- (parameters (identifier) @definition))
- (set! definition.kind "v"))
+ (parameters (identifier) @definition.var)))
;; Loops
((loop_expression
- (identifier) @definition)
- (set! definition.kind "v"))
+ (identifier) @definition.var))
;; Function definitions
;; Functions definitions creates both a definition and a new scope
@@ -28,16 +23,13 @@
(function_name
(function_name_field
(identifier) @definition.associated
- (property_identifier) @definition))) @scope
- (set! definition.kind "m"))
+ (property_identifier) @definition.method))) @scope)
((function
- (function_name (identifier) @definition)) @scope
- (set! definition.kind "f"))
+ (function_name (identifier) @definition.function)) @scope)
((local_function
- (identifier) @definition) @scope
- (set! definition.kind "f"))
+ (identifier) @definition.function) @scope)
((if_statement) @scope)
((for_in_statement) @scope)
diff --git a/queries/ruby/highlights.scm b/queries/ruby/highlights.scm
new file mode 100644
index 000000000..22e2267ed
--- /dev/null
+++ b/queries/ruby/highlights.scm
@@ -0,0 +1,140 @@
+; Keywords
+
+"alias" @keyword
+"and" @keyword
+"begin" @keyword
+"break" @keyword
+"case" @keyword
+"class" @keyword
+"def" @keyword
+"do" @keyword
+"else" @keyword
+"elsif" @keyword
+"end" @keyword
+"ensure" @keyword
+"for" @keyword
+"if" @keyword
+"in" @keyword
+"module" @keyword
+"next" @keyword
+"or" @keyword
+"rescue" @keyword
+"retry" @keyword
+"return" @keyword
+"then" @keyword
+"unless" @keyword
+"until" @keyword
+"when" @keyword
+"while" @keyword
+"yield" @keyword
+
+((identifier) @keyword
+ (match? @keyword "^(private|protected|public)$"))
+
+; Function calls
+
+((identifier) @function
+ (eq? @function "require"))
+
+"defined?" @function
+
+(call
+ receiver: (constant) @constant)
+(method_call
+ receiver: (constant) @constant)
+(call
+ method: (identifier) @function)
+(method_call
+ method: (identifier) @function)
+(call
+ method: (constant) @function)
+(method_call
+ method: (constant) @function)
+
+; Function definitions
+
+(alias (identifier) @function)
+(setter (identifier) @function)
+(method name: (identifier) @function)
+(method name: (constant) @constant)
+(class name: (constant) @constant)
+(singleton_method name: (identifier) @function)
+(singleton_method name: (constant) @constant)
+
+; Identifiers
+
+(class_variable) @Identifier
+(instance_variable) @Identifier
+
+((identifier) @constant
+ (match? @constant "^__(FILE|LINE|ENCODING)__$"))
+
+((constant) @constant
+ (match? @constant "^[A-Z\\d_]+$"))
+
+(constant) @constant
+
+(self) @constant
+(super) @Identifier
+
+(method_parameters (identifier) @Type)
+(lambda_parameters (identifier) @Type)
+(block_parameters (identifier) @Type)
+(splat_parameter (identifier) @Type)
+(hash_splat_parameter (identifier) @Type)
+(optional_parameter (identifier) @Type)
+(destructured_parameter (identifier) @Type)
+(block_parameter (identifier) @Type)
+(keyword_parameter (identifier) @Type)
+
+((identifier) @function
+ (is-not? local))
+
+; Literals
+
+(string) @string
+(bare_string) @string
+(bare_symbol) @constant
+(subshell) @string
+(heredoc_beginning) @Delimiter
+(heredoc_body) @string
+(heredoc_end) @Delimiter
+(symbol) @constant
+(regex) @string
+(escape_sequence) @Special
+(integer) @number
+(float) @number
+
+(nil) @Identifier
+(true) @Identifier
+(false) @Identifier
+
+(interpolation
+ "#{" @Delimiter
+ (identifier) @Identifier
+ "}" @Delimiter) @embedded
+
+(comment) @comment
+
+; Operators
+
+"=" @operator
+"=>" @operator
+"->" @operator
+"+" @operator
+"-" @operator
+"*" @operator
+"/" @operator
+
+"," @Normal
+";" @Normal
+"." @Normal
+
+"(" @Normal
+")" @Normal
+"[" @Normal
+"]" @Normal
+"{" @Normal
+"}" @Normal
+"%w(" @Normal
+"%i(" @Normal
diff --git a/queries/ruby/locals.scm b/queries/ruby/locals.scm
new file mode 100644
index 000000000..6c1b25238
--- /dev/null
+++ b/queries/ruby/locals.scm
@@ -0,0 +1,45 @@
+; The MIT License (MIT)
+;
+; Copyright (c) 2016 Rob Rix
+;
+; Permission is hereby granted, free of charge, to any person obtaining a copy
+; of this software and associated documentation files (the "Software"), to deal
+; in the Software without restriction, including without limitation the rights
+; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the Software is
+; furnished to do so, subject to the following conditions:
+;
+; The above copyright notice and this permission notice shall be included in all
+; copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+; SOFTWARE.
+
+;;; DESCLARATIONS AND SCOPES
+((method) @scope
+ (set! scope-inherits false))
+
+(block) @scope
+(do_block) @scope
+
+(method_parameters (identifier) @definition)
+(lambda_parameters (identifier) @definition)
+(block_parameters (identifier) @definition)
+(splat_parameter name: (identifier) @definition)
+(hash_splat_parameter name: (identifier) @definition)
+(optional_parameter name: (identifier) @definition)
+(destructured_parameter name: (identifier) @definition)
+(block_parameter name: (identifier) @definition)
+(keyword_parameter name: (identifier) @definition)
+
+(identifier) @reference
+
+(assignment left:(identifier) @definition)
+(left_assignment_list (identifier) @definition)
+(rest_assignment (identifier) @definition)
+(destructured_left_assignment (identifier) @definition)