nvim-treesitter-context icon indicating copy to clipboard operation
nvim-treesitter-context copied to clipboard

Allow jumping to nearest context with go_to_context()

Open toupeira opened this issue 1 year ago • 19 comments

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.

toupeira avatar Jun 17 '24 22:06 toupeira

I also really want this, will take a look and see if I can come with a neat proposal @toupeira :)

isakbm avatar May 25 '25 19:05 isakbm

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

toupeira avatar May 25 '25 19:05 toupeira

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 ?

isakbm avatar May 25 '25 19:05 isakbm

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

isakbm avatar May 25 '25 19:05 isakbm

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.

toupeira avatar May 25 '25 19:05 toupeira

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 avatar May 25 '25 19:05 isakbm

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

toupeira avatar May 25 '25 20:05 toupeira

I see that the entire context is readily available, so I will probably have something soon enough, will be fun :)

isakbm avatar May 25 '25 20:05 isakbm

Alright @toupeira, check it out, this is what you wanted right?

Image

I'll have a PR soon

isakbm avatar May 25 '25 21:05 isakbm

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 avatar May 25 '25 21:05 isakbm

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

toupeira avatar May 25 '25 21:05 toupeira

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 avatar May 25 '25 21:05 lewis6991

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

toupeira avatar May 25 '25 22:05 toupeira

Sorry I didn't mean that's what it does now, but what it should do.

lewis6991 avatar May 25 '25 22:05 lewis6991

Oh right sorry, in that case I fully agree 😀 I don't think it should hold up the PR though.

toupeira avatar May 25 '25 22:05 toupeira

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 🤞

isakbm avatar May 25 '25 23:05 isakbm

Please let me know if there's anything specific stopping this from getting merged in @lewis6991 :)

isakbm avatar May 26 '25 12:05 isakbm

how do you feel about this @toupeira ?

https://github.com/nvim-treesitter/nvim-treesitter-context/pull/603#discussion_r2107497713

isakbm avatar May 26 '25 15:05 isakbm

The option to pick between and bind to different keys the behavior of go_to_context is now removed.

isakbm avatar May 26 '25 15:05 isakbm