help_tags() does not escape special characters
Description
using Telescope help_tags to jump to [==] (equivalence class) opens help for = instead.
Neovim version
NVIM v0.9.2
Build type: RelWithDebInfo
LuaJIT 2.1.1694082368
Operating system and version
Windows 10
Telescope version / branch / rev
1bb28df
checkhealth telescope
==============================================================================
Telescope: require("Telescope.health").check()
Checking for required plugins ~
- OK plenary installed.
- OK nvim-treesitter installed.
Checking external dependencies ~
- ERROR rg: not found. `live-grep` finder will not function without [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) installed.
- WARNING fd: not found. Install [sharkdp/fd](https://github.com/sharkdp/fd) for extended capabilities
===== Installed extensions ===== ~
Steps to reproduce
nvim -nu minimal.lua :Telescope help_tags<CR> [==]<CR>
Expected behavior
help_tags opens help for [==] (in runtime/doc/pattern.txt),
the same file that is displayed in the preview.
Actual behavior
Help is opened for = (in runtime/doc/change.txt)
Minimal config
vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=/tmp/nvim/site]]
local package_root = '/tmp/nvim/site/pack'
local install_path = package_root .. '/packer/start/packer.nvim'
local function load_plugins()
require('packer').startup {
{
'wbthomason/packer.nvim',
{
'nvim-telescope/telescope.nvim',
requires = {
'nvim-lua/plenary.nvim',
{ 'nvim-telescope/telescope-fzf-native.nvim', run = 'make' },
},
},
},
config = {
package_root = package_root,
compile_path = install_path .. '/plugin/packer_compiled.lua',
display = { non_interactive = true },
},
}
end
_G.load_config = function()
require('telescope').setup()
end
if vim.fn.isdirectory(install_path) == 0 then
print("Installing Telescope and dependencies.")
vim.fn.system { 'git', 'clone', '--depth=1', 'https://github.com/wbthomason/packer.nvim', install_path }
end
load_plugins()
require('packer').sync()
vim.cmd [[autocmd User PackerComplete ++once echo "Ready!" | lua load_config()]]
:help for 'help' contains this paragraph:
{subject} can include wildcards such as "*", "?" and
"[a-z]":
:help z? jump to help for any "z" command
:help z. jump to the help for "z."
But when a tag exists it is taken literally:
:help :? jump to help for ":?"
And in help [==]@en produced by telescope, '[' and ']' are treated as special characters.
https://github.com/nvim-telescope/telescope.nvim/blob/1bb28df3cfc241b961331f00dcb8d5b45fe3e4f0/lua/telescope/builtin/__internal.lua#L778-L784
I think this might work for all characters, although I couldn't test it for non-ascii:
local lang = 'en'
local subject= '[==]'
local code_points = vim.fn.str2list(subject)
local escaped_str = '\\v'
for _, code in ipairs(code_points) do
escaped_str = escaped_str .. '%U' .. string.format('%x', code)
end
escaped_str = escaped_str .. '@' .. lang
vim.cmd.help(escaped_str)
Oof, I'm almost tempted to say "it's not our problem" considering :h [==] nets the same result...
Your solution is pretty interesting though - first time actually learning about all this "magic". But I think your concern is valid - particularly for non-utf8 encodings...
couldn't test it for non-ascii
I tested it and it works for multibyte characters.
I also wrote a test to see which other help tags are affected: Results (for 0.9.5):
1605 :[vV\x16] autocmd.txt
autocmd.txt:755
repeat.txt:49
4482 [+cmd] editing.txt
editing.txt:489
motion.txt:315
4483 [..] pattern.txt
pattern.txt:1179
repeat.txt:16
4505 [==] pattern.txt
pattern.txt:1174
change.txt:564
8485 s/\= change.txt
change.txt:896
change.txt:184
8489 s/\\ change.txt
change.txt:813
change.txt:785
How to test:
# move tags file from neovim's runtime/doc/tags to current directory
nvim --clean -u tags-test.lua
tags-test.lua
-- quite slow!
local function goto_fixed(tag)
local lang = 'en'
local code_points = vim.fn.str2list(tag)
local escaped_str = '\\v'
for _, code in ipairs(code_points) do
escaped_str = escaped_str .. '%U' .. string.format('%x', code)
end
escaped_str = escaped_str .. '@' .. lang
vim.cmd.help(escaped_str)
end
local function goto_orig(tag)
vim.cmd.help(tag .. '@en')
end
local function get_pos()
local name = vim.fn.expand('%:t')
local lnum = vim.fn.line('.')
return name .. ':' .. lnum
end
vim.cmd('syntax off')
vim.cmd('set syntax=off')
local count = 0
local lines = vim.fn.readfile('tags')
Result = {}
for i, line in ipairs(lines) do
local tag, file, _ = unpack(vim.split(line, '\t'))
goto_fixed(tag)
local fixed_pos = get_pos()
goto_orig(tag)
local orig_pos = get_pos()
if fixed_pos ~= orig_pos then
table.insert(Result, i..' '..tag..' '..file)
table.insert(Result, fixed_pos)
table.insert(Result, orig_pos)
table.insert(Result, '')
end
::next::
count = count + 1
if count == 100 then
print(i, 'of', #lines)
vim.cmd('redraw!')
count = 0
end
end
vim.cmd('close')
print('Done')
vim.api.nvim_buf_set_lines(0, 0, 0, false, Result)