diff options
| author | Thomas Vigouroux <39092278+vigoux@users.noreply.github.com> | 2020-05-03 11:19:28 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-03 11:19:28 +0200 |
| commit | 5cc7407c7f730c552120fc7d7d9a136ae6b7035f (patch) | |
| tree | 593390eedb3dd2218a2a143e8d5dbcd566780664 | |
| parent | Merge pull request #39 from vigoux/incremental_selection (diff) | |
| parent | Add documentation for `node_movement`, rename: `textobj` -> `incremental_sele... (diff) | |
| download | nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar.gz nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar.bz2 nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar.lz nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar.xz nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.tar.zst nvim-treesitter-5cc7407c7f730c552120fc7d7d9a136ae6b7035f.zip | |
Merge pull request #37 from theHamsta/node-movement
Add 'nvim-treesitter/node-movement'
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | doc/nvim-treesitter.txt | 36 | ||||
| -rw-r--r-- | lua/nvim-treesitter/configs.lua | 11 | ||||
| -rw-r--r-- | lua/nvim-treesitter/node_movement.lua | 91 | ||||
| -rw-r--r-- | lua/nvim-treesitter/utils.lua | 59 |
5 files changed, 196 insertions, 14 deletions
@@ -92,7 +92,7 @@ require'nvim-treesitter.configs'.setup { enable = true, -- false will disable the whole extension disable = { 'c', 'rust' }, -- list of language that will be disabled }, - textobj = { -- this enables incremental selection + incremental_selection = { -- this enables incremental selection enable = true, disable = { 'cpp', 'lua' }, keymaps = { -- mappings for incremental selection (visual mappings) @@ -100,6 +100,16 @@ require'nvim-treesitter.configs'.setup { 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 } EOF @@ -124,6 +134,7 @@ Some of these features are : - [x] Incremental selection - [ ] Syntax based code folding - [x] Consistent syntax highlighting (the api is not quite stable yet) + - [x] Cursor movement in node hierachy 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! diff --git a/doc/nvim-treesitter.txt b/doc/nvim-treesitter.txt index 55ada1596..ceb834be1 100644 --- a/doc/nvim-treesitter.txt +++ b/doc/nvim-treesitter.txt @@ -30,19 +30,29 @@ By default, everything is disabled. To enable support for features, in your `ini > 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 - }, - textobj = { -- this enables incremental selection - enable = true, - disable = { 'cpp', 'lua' }, - keymaps = { -- mappings for visual selection - node_incremental = "<leader>e", -- "grn" by default, - scope_incremental = "<leader>f" -- "grc" by default - } - }, - ensure_installed = 'all' -- can be one of 'all', 'language' or {'language1', 'language2' ... } + 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 } < diff --git a/lua/nvim-treesitter/configs.lua b/lua/nvim-treesitter/configs.lua index 10dcc9605..9695da5af 100644 --- a/lua/nvim-treesitter/configs.lua +++ b/lua/nvim-treesitter/configs.lua @@ -223,6 +223,17 @@ local config = { }, 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 = {}, 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/utils.lua b/lua/nvim-treesitter/utils.lua index 391863f0d..9d591eab6 100644 --- a/lua/nvim-treesitter/utils.lua +++ b/lua/nvim-treesitter/utils.lua @@ -93,4 +93,63 @@ function M.smallest_containing_scope(node, bufnr) 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 |
