nvim-treesitter / nvim-treesitter/nvim-treesitter-textobjects

Visual mode: Inconsistent behavior with built-in text object selection

Open
#904 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Tree-sitter Query
Stars
2.8k
Forks
271
Avg merge
8d 8h
Merged PRs (30d)
1

Description

Describe the bug
When in Visual mode, doing ap or aw consecutively will highlight more and more; the current selection doesn't get reset.

This stands in contrast with how my af keybinding for "around function" is currently behaving; if I move and use it a second time to try to include another function, it instead resets the selection to only the new function.

Perhaps I've misconfigured something somehow?

If this is somehow expected behavior, is there a workaround?
My usecase is deleting two functions right next to each other, so I can move them somewhere else in one paste, instead of moving them one after the other, or relying on register shenanigans.

Output of :checkhealth nvim-treesitter

==============================================================================
nvim-treesitter: ✅

Requirements ~

  • ✅ OK Neovim was compiled with tree-sitter runtime ABI version 15 (required >=13).
  • ✅ OK tree-sitter-cli 0.26.9 (/usr/bin/tree-sitter)
  • ✅ OK tar 1.35.0 (/usr/bin/tar)
  • ✅ OK curl 8.21.0 (/usr/bin/curl)
    curl 8.21.0 (x86_64-pc-linux-gnu) libcurl/8.21.0 OpenSSL/3.6.4 zlib/1.3.1.zlib-ng brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.5 libssh2/1.11.1 nghttp2/1.70.0 ngtcp2/1.25.0 nghttp3/1.18.0 mit-krb5/1.22.2
    Release-Date: 2026-06-24
    Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt mqtts pop3 pop3s rtsp scp sftp smtp smtps telnet tftp ws wss
    Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd

OS Info ~

  • machine: x86_64
  • release: 7.2.0-1-cachyos
  • sysname: Linux
  • version: #1 SMP PREEMPT_DYNAMIC Thu, 20 Aug 2026 16:18:38 +0000

Install directory for parsers and queries ~

  • /home/demorome/.local/share/nvim/site
  • ✅ OK is writable.
  • ✅ OK is in runtimepath.

Installed languages H L F I J ~

  • bash ✓ ✓ ✓ ✓ ✓

  • c ✓ ✓ ✓ ✓ ✓

  • c_sharp ✓ ✓ ✓ . ✓

  • diff ✓ . ✓ . ✓

  • dtd ✓ ✓ ✓ . ✓

  • fish ✓ ✓ ✓ ✓ ✓

  • gitattributes ✓ ✓ . . ✓

  • gitignore ✓ . . . ✓

  • html ✓ ✓ ✓ ✓ ✓

  • html_tags

  • ini ✓ . ✓ . ✓

  • json ✓ ✓ ✓ ✓ ✓

  • kitty ✓ . . . ✓

  • lua ✓ ✓ ✓ ✓ ✓

  • luadoc ✓ . . . .

  • markdown ✓ . ✓ ✓ ✓

  • markdown_inline ✓ . . . ✓

  • query ✓ ✓ ✓ ✓ ✓

  • tmux

  • vim ✓ ✓ ✓ . ✓

  • vimdoc ✓ . . . ✓

  • xml ✓ ✓ ✓ ✓ ✓

    Legend: [H]ighlights, [L]ocals, [F]olds, [I]ndents, In[J]ections ~

Output of nvim --version

NVIM v0.12.5
Build type: RelWithDebInfo
LuaJIT 2.1.1787165859
Run "nvim -V1 -v" for more info

Additional context
Config:

vim.pack.add { 'https://github.com/nvim-treesitter/nvim-treesitter-textobjects' }
require('nvim-treesitter-textobjects').setup {
  select = {
    -- Automatically jump forward to textobj, similar to targets.vim
    lookahead = true,
    -- You can choose the select mode (default is charwise 'v')
    --
    -- Can also be a function which gets passed a table with the keys
    -- * query_string: eg '@function.inner'
    -- * method: eg 'v' or 'o'
    -- and should return the mode ('v', 'V', or '<c-v>') or a table
    -- mapping query_strings to modes.
    selection_modes = {
      --['@parameter.outer'] = 'v', -- charwise
      --['@function.outer'] = 'V', -- linewise
      -- ['@class.outer'] = '<c-v>', -- blockwise
    },
    -- If you set this to `true` (default is `false`) then any textobject is
    -- extended to include preceding or succeeding whitespace. Succeeding
    -- whitespace has priority in order to act similarly to eg the built-in
    -- `ap`.
    --
    -- Can also be a function which gets passed a table with the keys
    -- * query_string: eg '@function.inner'
    -- * selection_mode: eg 'v'
    -- and should return true of false
    include_surrounding_whitespace = false,
  },
}

-- You can use the capture groups defined in `textobjects.scm`
vim.keymap.set(
  { 'x', 'o' },
  'af',
  function() require('nvim-treesitter-textobjects.select').select_textobject('@function.outer', 'textobjects') end,
  { desc = 'Around [F]unction' }
)
-- other irrelevant keymaps

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the configured select.select_textobject entry point and the af mapping shown in the issue, then reproduce consecutive ap/aw selections in Visual mode and compare them with af. Done means the selection behavior is consistent or the expected workaround is documented, with a regression test covering repeated text-object selection.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua, neovim
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.