Allow jumping to nearest context with go_to_context()
I wanted to have a mapping to jump to whatever is the current "parent" node, and found the go_to_context() function which works great! But it only jumps to contexts that are also displayed by the plugin, and I'd like this mapping to be more granular and also consider contexts that are in view, or hidden by my max_lines setting.
I managed to hack something together if I expose the iter_context_parents function from the plugin:
-- add in `lua/treesitter-context/context.lua`
M.iter_context_parents = iter_context_parents
-- in my mapping:
local context = require('treesitter-context.context')
local cursor = vim.api.nvim_win_get_cursor(0)
local row = cursor[1]
local parents, _query = context.iter_context_parents(0, { row - 1, 0, row - 1, 0 })()
for i = 1, #parents do
local parent_row = parents[i]:range() + 1
if parent_row < row then
vim.cmd([[ normal! m' ]])
vim.api.nvim_win_set_cursor(0, { parent_row, 0 })
vim.cmd.normal('^')
return
end
end
This seems to work how I want, and now I wonder what it would take to implement a cleaner version of this inside the plugin, or what bits could be exposed to allow a custom mapping like this (iter_context_parents seems a bit too low-level).
Also not sure if there's a way to do this using e.g. nvim-treesitter-textobjects, it seems the magic sauce that's needed are the queries collected in this plugin.
I also really want this, will take a look and see if I can come with a neat proposal @toupeira :)
@isakbm nice! 👍
FWIW I setup something similar in nvim-treesitter-textobjects to jump to "scopes":
textobjects = {
move = {
enable = true,
goto_next_start = {
[']s'] = { query = '@local.scope', query_group = 'locals', desc = 'Next scope' },
},
goto_next_end = {
[']S'] = { query = '@local.scope', query_group = 'locals', desc = 'End of scope' },
},
goto_previous_start = {
['[s'] = { query = '@local.scope', query_group = 'locals', desc = 'Previous scope' },
},
goto_previous_end = {
['[S'] = { query = '@local.scope', query_group = 'locals', desc = 'End of previous scope' },
},
},
},
But it doesn't take into account the hierarchy of scopes, so it's not always obvious where you end up 😀
I also want to check out https://github.com/aaronik/treewalker.nvim and see how its :Treewalker Up command behaves.
But it doesn't take into account the hierarchy of scopes, so it's not always obvious where you end up 😀
Oh what you do you mean by this exactly @toupeira ?
I think I know what you mean now, is it something like this... ?
local function bat()
local function foo()
end
-- when cursor here we want to jump to bat()
-- not foo()
local function bar()
end
end
Ah yes exactly like that, was trying to come up with a good example too 😀 The other downside is that different languages have different opinions on what @local.scope means, e.g. in Lua if is a scope but in Bash it's not.
Those different language opionions on @local.scope, don't they already impact treesitter-context, or would you say that somehow treesitter-context for the moment somehow works around those quirks?
@isakbm AFAIK treesitter-context uses its own queries in https://github.com/nvim-treesitter/nvim-treesitter-context/tree/master/queries to define what "context" means for different languages.
I see that the entire context is readily available, so I will probably have something soon enough, will be fun :)
Alright @toupeira, check it out, this is what you wanted right?
I'll have a PR soon
Test it out yourself @toupeira -> PR https://github.com/nvim-treesitter/nvim-treesitter-context/pull/603
I think I did this in the dumbest and simplest way possible.
vim.keymap.set("n", "[c", function()
require("treesitter-context").go_to_context(vim.v.count1, "entire window")
end, { silent = true })
The naming of the modes context window and entire window might be a bit esoteric and confusing, but it is truly what the difference is though. Because it does choose the nearest parent in both cases, just the set of parents to choose from is either context window (default) or entire window (opt in).
@isakbm awesome, thanks! :heart:
I tested your PR and everything seems to work as expected. It would be nice if it moved to the first character on the target line (rather than the first column), but I can work around that easily enough with vim.cmd.normal('^') in the mapping (as I did in my original example).
It would be nice if it moved to the first character on the target line
It should move the first character of the context node since lines can contain more than one context.
@lewis6991 hmm it doesn't for me though (same in master), and if I log the contexts at https://github.com/nvim-treesitter/nvim-treesitter-context/blob/4976d8b90401cba9b85f6861e4e5a6edef2f2086/lua/treesitter-context.lua#L337 the columns are always zero.
Are you on Neovim nightly perhaps, or nvim-treesitter 1.0? I'm on 0.11.1 and still using the master branches for the TS plugins.
Sorry I didn't mean that's what it does now, but what it should do.
Oh right sorry, in that case I fully agree 😀 I don't think it should hold up the PR though.
Interesting default behavior then. I didn't modify the existing logic, just extended the parent ranges list.
Either way I would think addressing that cursor positioning should be a separate issue.
I've addressed all of the points you brought up in the review @lewis6991 🤞
Please let me know if there's anything specific stopping this from getting merged in @lewis6991 :)
how do you feel about this @toupeira ?
https://github.com/nvim-treesitter/nvim-treesitter-context/pull/603#discussion_r2107497713
The option to pick between and bind to different keys the behavior of go_to_context is now removed.