feat: All buffer previewers are now async and more config options (#354)

Configure preview window with:
autocmd User TelescopePreviewerLoaded setlocal wrap
autocmd User TelescopePreviewerLoaded setlocal number

file_maker example: Use regex highlighting for certain filetype like `*min.js` because they slow
down things with treesitter highlighter. Just a snippet for tests. We will do an extension :)

local previewers = require('telescope.previewers')
local putils = require('telescope.previewers.utils')
local pfiletype = require('plenary.filetype')

local _bad = { '.*%.min%.js' }
local bad_files = function(filepath)
  for _, v in ipairs(_bad) do
    if filepath:match(v) then
      return true
    end
  end

  return false
end

local new_maker = function(filepath, bufnr, bufname, use_ft_detect, callback)
  if use_ft_detect == nil then use_ft_detect = true end

  if bad_files(filepath) then
    previewers.buffer_previewer_maker(filepath, bufnr, bufname, false, callback)
    local ft = pfiletype.detect(filepath)
    putils.regex_highlighter(bufnr, ft)
  else
    previewers.buffer_previewer_maker(filepath, bufnr, bufname, use_ft_detect, callback)
  end
end

require('telescope').setup {
  defaults = {
    buffer_previewer_maker = new_maker,
  }
}
This commit is contained in:
Simon Hauser
2020-12-29 21:05:59 +01:00
committed by GitHub
parent e555cd375f
commit 1d40ab5ccd
8 changed files with 296 additions and 148 deletions

View File

@@ -162,6 +162,9 @@ require('telescope').setup{
file_previewer = require'telescope.previewers'.cat.new, -- For buffer previewer use `require'telescope.previewers'.vim_buffer_cat.new`
grep_previewer = require'telescope.previewers'.vimgrep.new, -- For buffer previewer use `require'telescope.previewers'.vim_buffer_vimgrep.new`
qflist_previewer = require'telescope.previewers'.qflist.new, -- For buffer previewer use `require'telescope.previewers'.vim_buffer_qflist.new`
-- Developer configurations: Not meant for general override
buffer_previewer_maker = require'telescope.previewers'.buffer_previewer_maker
}
}
```
@@ -203,6 +206,12 @@ EOF
| `grep_previewer` | What telescope previewer to use for grep and similar | [Previewers](#previewers) |
| `qflist_previewer` | What telescope previewer to use for qflist | [Previewers](#previewers) |
### Options for extension developers
| Keys | Description | Options |
|------------------------|-------------------------------------------------------|----------------------------|
| `buffer_previewer_maker` | How a file gets loaded and which highlighter will be used. Extensions will change it | function |
### Options affecting Sorting
| Keys | Description | Options |
@@ -448,10 +457,17 @@ Built-in functions. Ready to be bound to any key you like. :smile:
By default, telescope.nvim uses `cat`/`bat` for preview. However after telescope's new experimental previewers
are stable this will change. The new experimental previewers use tree-sitter and vim buffers, provide much
better performance and are ready for daily usage (`vim_buffer_cat` more than the others), but there might be
cases where it can't detect a Filetype correctly, thus leading to wrong highlights.
Also `vimgrep` and `qflist` might be slower due to synchronous file loading.
better performance and are ready for daily usage, but there might be cases where it can't detect a Filetype
correctly, thus leading to wrong highlights. This is because we can't determine the filetype in the traditional way
(we don't do `bufload`. We read the file async with `vim.loop.fs_` and attach only a highlighter), because we can't
execute autocommands, otherwise the speed of the previewer would slow down considerably.
If you want to configure more filetypes take a look at
[plenary wiki](https://github.com/nvim-lua/plenary.nvim#plenaryfiletype).
If you want to configure the `vim_buffer_` previewer, e.g. you want the line to wrap do this:
```vim
autocmd User TelescopePreviewerLoaded setlocal wrap
```
## Sorters

View File

@@ -96,6 +96,7 @@ function config.set_defaults(defaults)
set("file_previewer", function(...) return require('telescope.previewers').cat.new(...) end)
set("grep_previewer", function(...) return require('telescope.previewers').vimgrep.new(...) end)
set("qflist_previewer", function(...) return require('telescope.previewers').qflist.new(...) end)
set("buffer_previewer_maker", function(...) return require('telescope.previewers').buffer_previewer_maker(...) end)
end
function config.clear_defaults()

View File

@@ -270,7 +270,7 @@ function make_entry.gen_from_git_commits()
local make_display = function(entry)
return displayer {
{entry.value, "TelescopeResultsNumber"},
{entry.value, "TelescopeResultsIdentifier"},
entry.msg
}
end

View File

@@ -1,60 +1,34 @@
local debounce = require('telescope.debounce')
local from_entry = require('telescope.from_entry')
local path = require('telescope.path')
local utils = require('telescope.utils')
local putils = require('telescope.previewers.utils')
local Previewer = require('telescope.previewers.previewer')
local conf = require('telescope.config').values
local pfiletype = require('plenary.filetype')
local has_ts, _ = pcall(require, 'nvim-treesitter')
local _, ts_highlight = pcall(require, 'nvim-treesitter.highlight')
local _, ts_parsers = pcall(require, 'nvim-treesitter.parsers')
local buf_delete = utils.buf_delete
local defaulter = utils.make_default_callable
local previewers = {}
local previewer_ns = vim.api.nvim_create_namespace('telescope.previewers')
local ns_previewer = vim.api.nvim_create_namespace('telescope.previewers')
local file_maker_async = function(filepath, bufnr, bufname, callback)
local ft = pfiletype.detect(filepath)
previewers.file_maker = function(filepath, bufnr, bufname, use_ft_detect, callback)
if use_ft_detect == nil then use_ft_detect = true end
local ft = use_ft_detect and pfiletype.detect(filepath)
if bufname ~= filepath then
path.read_file_async(filepath, vim.schedule_wrap(function(data)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, vim.split(data, "\n"))
if callback then callback() end
if callback then callback(bufnr) end
end))
else
if callback then callback() end
if callback then callback(bufnr) end
end
if ft ~= '' then
if has_ts and ts_parsers.has_parser(ft) then
ts_highlight.attach(bufnr, ft)
else
vim.cmd(':ownsyntax ' .. ft)
end
end
end
local file_maker_sync = function(filepath, bufnr, bufname)
local ft = pfiletype.detect(filepath)
if bufname ~= filepath then
local data = path.read_file(filepath)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, vim.split(data, "\n"))
end
if ft ~= '' then
if has_ts and ts_parsers.has_parser(ft) then
ts_highlight.attach(bufnr, ft)
else
vim.cmd(':ownsyntax ' .. ft)
end
end
putils.highlighter(bufnr, ft)
end
previewers.new_buffer_previewer = function(opts)
@@ -69,6 +43,8 @@ previewers.new_buffer_previewer = function(opts)
local old_bufs = {}
local bufname_table = {}
local global_state = require'telescope.state'
local function get_bufnr(self)
if not self.state then return nil end
return self.state.bufnr
@@ -100,13 +76,23 @@ previewers.new_buffer_previewer = function(opts)
opt_teardown(self)
end
local last_nr
if opts.keep_last_buf then
last_nr = global_state.get_global_key('last_preview_bufnr')
-- Push in another buffer so the last one will not be cleaned up
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_win_set_buf(self.state.winid, bufnr)
end
set_bufnr(self, nil)
set_bufname(self, nil)
for _, bufnr in ipairs(old_bufs) do
if bufnr ~= last_nr then
buf_delete(bufnr)
end
end
end
function opts.preview_fn(self, entry, status)
if get_bufnr(self) == nil then
@@ -134,8 +120,14 @@ previewers.new_buffer_previewer = function(opts)
self.state.bufname = nil
end
if opts.keep_last_buf then global_state.set_global_key("last_preview_bufnr", self.state.bufnr) end
opts.define_preview(self, entry, status)
putils.with_preview_window(status, nil, function()
vim.cmd'do User TelescopePreviewerLoaded'
end)
if opts.get_buffer_by_name then
set_bufname(self, opts.get_buffer_by_name(self, entry))
end
@@ -180,7 +172,7 @@ previewers.cat = defaulter(function(_)
putils.with_preview_window(status, nil, function()
local p = from_entry.path(entry, true)
if p == nil or p == '' then return end
file_maker_async(p, self.state.bufnr, self.state.bufname)
conf.buffer_previewer_maker(p, self.state.bufnr, self.state.bufname)
end)
end
}
@@ -196,7 +188,7 @@ previewers.vimgrep = defaulter(function(_)
teardown = function(self)
if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then
vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, previewer_ns, 0, -1)
vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1)
end
end,
@@ -210,17 +202,18 @@ previewers.vimgrep = defaulter(function(_)
local p = from_entry.path(entry, true)
if p == nil or p == '' then return end
file_maker_sync(p, self.state.bufnr, self.state.bufname)
if lnum ~= 0 then
if self.state.last_set_bufnr then
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, previewer_ns, 0, -1)
end
pcall(vim.api.nvim_buf_add_highlight, self.state.bufnr, previewer_ns, "TelescopePreviewLine", lnum - 1, 0, -1)
pcall(vim.api.nvim_win_set_cursor, status.preview_win, {lnum, 0})
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1)
end
self.state.last_set_bufnr = self.state.bufnr
conf.buffer_previewer_maker(p, self.state.bufnr, self.state.bufname, true, function(bufnr)
if lnum ~= 0 then
pcall(vim.api.nvim_buf_add_highlight, bufnr, ns_previewer, "TelescopePreviewLine", lnum - 1, 0, -1)
pcall(vim.api.nvim_win_set_cursor, self.state.winid, {lnum, 0})
end
self.state.last_set_bufnr = bufnr
end)
end)
end
}
@@ -248,14 +241,18 @@ previewers.ctags = defaulter(function(_)
scode = string.gsub(scode, [[\/]], [[/]])
scode = string.gsub(scode, '[*]', [[\*]])
file_maker_sync(entry.filename, self.state.bufnr, self.state.bufname)
conf.buffer_previewer_maker(entry.filename, self.state.bufnr, self.state.bufname, true, function(bufnr)
vim.api.nvim_buf_call(bufnr, function()
pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid)
vim.cmd "norm! gg"
vim.fn.search(scode)
vim.fn.search(scode, "W")
vim.cmd "norm! zz"
self.state.hl_id = vim.fn.matchadd('TelescopePreviewMatch', scode)
end)
end)
end)
end
}
end, {})
@@ -287,13 +284,18 @@ previewers.builtin = defaulter(function(_)
text = entry.text:gsub('_', '.', 1)
end
file_maker_sync(entry.filename, self.state.bufnr, self.state.bufname)
conf.buffer_previewer_maker(entry.filename, self.state.bufnr, self.state.bufname, true, function(bufnr)
vim.api.nvim_buf_call(bufnr, function()
pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid)
vim.cmd "norm! gg"
vim.fn.search(text)
vim.fn.search(text, "W")
vim.cmd "norm! zz"
self.state.hl_id = vim.fn.matchadd('TelescopePreviewMatch', text)
end)
end)
end)
end
}
@@ -322,51 +324,150 @@ previewers.help = defaulter(function(_)
query = query:sub(2)
query = [[\V]] .. query
file_maker_sync(entry.filename, self.state.bufnr, self.state.bufname)
conf.buffer_previewer_maker(entry.filename, self.state.bufnr, self.state.bufname, false, function(bufnr)
vim.api.nvim_buf_call(bufnr, function()
vim.cmd(':ownsyntax help')
pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid)
vim.cmd "norm! gg"
vim.fn.search(query, "W")
vim.cmd "norm! zz"
self.state.hl_id = vim.fn.matchadd('TelescopePreviewMatch', query)
end)
end)
end)
end
}
end, {})
previewers.man = defaulter(function(_)
return previewers.new_buffer_previewer {
define_preview = debounce.throttle_leading(function(self, entry, status)
get_buffer_by_name = function(_, entry)
return entry.value
end,
define_preview = function(self, entry, status)
putils.with_preview_window(status, nil, function()
local cmd = entry.value
local man_value = vim.fn['man#goto_tag'](cmd, '', '')
if #man_value == 0 then
print("No value for:", cmd)
return
end
local filename = man_value[1].filename
if vim.api.nvim_buf_get_name(0) == filename then
return
end
vim.api.nvim_command('view ' .. filename)
vim.api.nvim_buf_set_option(self.state.bufnr, 'buftype', 'nofile')
vim.api.nvim_buf_set_option(self.state.bufnr, 'bufhidden', 'hide')
vim.api.nvim_buf_set_option(self.state.bufnr, 'swapfile', false)
vim.api.nvim_buf_set_option(self.state.bufnr, 'buflisted', false)
local win_width = vim.api.nvim_win_get_width(self.state.winid)
putils.job_maker({'man', '-P', 'cat', entry.value},
{ ["MANWIDTH"] = win_width },
entry.value,
self.state.bufnr,
self.state.bufname
)
putils.regex_highlighter(_, 'man')
end)
end, 5)
end
}
end)
previewers.git_branch_log = defaulter(function(_)
return previewers.new_buffer_previewer {
get_buffer_by_name = function(_, entry)
return entry.value
end,
define_preview = function(self, entry, status)
local highlight_buffer = function(bufnr, content)
if content and table.getn(content) == 0 then
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "No log found for branch: " .. entry.value })
elseif content and table.getn(content) > 1 then
for i = 1, #content do
local line = content[i]
local _, hash_start = line:find('[%*%s|]*')
if hash_start then
local hash_end = hash_start + 7
if hash_end < #line then
vim.api.nvim_buf_add_highlight(bufnr,
ns_previewer,
"TelescopeResultsIdentifier",
i - 1,
hash_start - 1,
hash_end
)
end
end
local _, cur_start = line:find('- %(')
if cur_start then
local cur_end = string.find(line, '%) ')
vim.api.nvim_buf_add_highlight(bufnr,
ns_previewer,
"TelescopeResultsConstant",
i - 1,
cur_start - 1,
cur_end
)
end
local date_start, _ = line:find(' %(%d')
if date_start then
vim.api.nvim_buf_add_highlight(bufnr,
ns_previewer,
"TelescopeResultsSpecialComment",
i - 1,
date_start,
#line
)
end
end
end
end
putils.with_preview_window(status, nil, function()
local cmd = { 'git', '-P', 'log', '--graph', '--pretty=format:%h -%d %s (%cr)',
'--abbrev-commit', '--date=relative', entry.value
}
putils.job_maker(cmd, nil, entry.value, self.state.bufnr, self.state.bufname, highlight_buffer)
end)
end
}
end, {})
previewers.git_commit_diff = defaulter(function(_)
return previewers.new_buffer_previewer {
get_buffer_by_name = function(_, entry)
return entry.value
end,
define_preview = function(self, entry, status)
putils.with_preview_window(status, nil, function()
putils.job_maker({ 'git', '-P', 'diff', entry.value .. '^!' },
nil,
entry.value,
self.state.bufnr,
self.state.bufname
)
putils.regex_highlighter(_, 'diff')
end)
end
}
end, {})
previewers.git_file_diff = defaulter(function(_)
return previewers.new_buffer_previewer {
get_buffer_by_name = function(_, entry)
return entry.value
end,
define_preview = function(self, entry, status)
putils.with_preview_window(status, nil, function()
putils.job_maker({ 'git', '-P', 'diff', entry.value },
nil,
entry.value,
self.state.bufnr,
self.state.bufname
)
putils.regex_highlighter(_, 'diff')
end)
end
}
end, {})
previewers.autocommands = defaulter(function(_)
return previewers.new_buffer_previewer {
teardown = function(self)
if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, previewer_ns, 0, -1)
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1)
end
end,
@@ -380,7 +481,7 @@ previewers.autocommands = defaulter(function(_)
end, status.picker.finder.results)
if self.state.last_set_bufnr then
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, previewer_ns, 0, -1)
pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1)
end
local selected_row = 0
@@ -412,7 +513,7 @@ previewers.autocommands = defaulter(function(_)
end
end
vim.api.nvim_buf_add_highlight(self.state.bufnr, previewer_ns, "TelescopePreviewLine", selected_row + 1, 0, -1)
vim.api.nvim_buf_add_highlight(self.state.bufnr, ns_previewer, "TelescopePreviewLine", selected_row + 1, 0, -1)
vim.api.nvim_win_set_cursor(status.preview_win, {selected_row + 1, 0})
self.state.last_set_bufnr = self.state.bufnr
@@ -424,11 +525,11 @@ previewers.highlights = defaulter(function(_)
return previewers.new_buffer_previewer {
teardown = function(self)
if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then
vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, previewer_ns, 0, -1)
vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1)
end
end,
get_buffer_by_name = function(_, entry)
get_buffer_by_name = function()
return "highlights"
end,
@@ -457,13 +558,13 @@ previewers.highlights = defaulter(function(_)
end
end
pcall(vim.api.nvim_buf_clear_namespace, self.state.bufnr, previewer_ns, 0, -1)
pcall(vim.api.nvim_buf_clear_namespace, self.state.bufnr, ns_previewer, 0, -1)
vim.cmd "norm! gg"
vim.fn.search(entry.value .. ' ')
local lnum = vim.fn.line('.')
-- That one is actually a match but its better to use it like that then matchadd
vim.api.nvim_buf_add_highlight(self.state.bufnr,
previewer_ns,
ns_previewer,
"TelescopePreviewMatch",
lnum - 1,
0,

View File

@@ -9,17 +9,18 @@ previewers.new = function(...)
end
previewers.new_termopen_previewer = term_previewer.new_termopen_previewer
previewers.git_commit_diff = term_previewer.git_commit_diff
previewers.git_branch_log = term_previewer.git_branch_log
previewers.git_file_diff = term_previewer.git_file_diff
previewers.cat = term_previewer.cat
previewers.vimgrep = term_previewer.vimgrep
previewers.qflist = term_previewer.qflist
previewers.new_buffer_previewer = buffer_previewer.new_buffer_previewer
previewers.buffer_previewer_maker = buffer_previewer.file_maker
previewers.vim_buffer_cat = buffer_previewer.cat
previewers.vim_buffer_vimgrep = buffer_previewer.vimgrep
previewers.vim_buffer_qflist = buffer_previewer.qflist
previewers.git_branch_log = buffer_previewer.git_branch_log
previewers.git_commit_diff = buffer_previewer.git_commit_diff
previewers.git_file_diff = buffer_previewer.git_file_diff
previewers.ctags = buffer_previewer.ctags
previewers.builtin = buffer_previewer.builtin
previewers.help = buffer_previewer.help

View File

@@ -187,15 +187,18 @@ previewers.new_termopen_previewer = function(opts)
return opts.get_command(entry, st)
else
local env = {}
local cmd = opts.get_command(entry, st)
if not cmd then return end
for k, v in pairs(termopen_env) do
table.insert(env, k .. '=' .. v)
end
return table.concat(env, ' ') .. ' ' .. table.concat(opts.get_command(entry, st), ' ')
return table.concat(env, ' ') .. ' ' .. table.concat(cmd, ' ')
end
end
putils.with_preview_window(status, bufnr, function()
set_term_id(self, vim.fn.termopen(get_cmd(status), term_opts))
local cmd = get_cmd(status)
if cmd then set_term_id(self, vim.fn.termopen(cmd, term_opts)) end
end)
vim.api.nvim_buf_set_name(bufnr, tostring(bufnr))
@@ -228,34 +231,6 @@ previewers.new_termopen_previewer = function(opts)
return Previewer:new(opts)
end
previewers.git_commit_diff = defaulter(function(_)
return previewers.new_termopen_previewer {
get_command = function(entry)
local sha = entry.value
return { 'git', '-p', 'diff', sha .. '^!' }
end
}
end, {})
previewers.git_branch_log = defaulter(function(_)
return previewers.new_termopen_previewer {
get_command = function(entry)
return { 'git', '-p', 'log', '--graph',
"--pretty=format:" .. add_quotes .. "%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset"
.. add_quotes,
'--abbrev-commit', '--date=relative', entry.value }
end
}
end, {})
previewers.git_file_diff = defaulter(function(_)
return previewers.new_termopen_previewer {
get_command = function(entry)
return { 'git', '-p', 'diff', entry.value }
end
}
end, {})
previewers.cat = defaulter(function(opts)
local maker = get_maker(opts)

View File

@@ -1,5 +1,11 @@
local context_manager = require('plenary.context_manager')
local has_ts, _ = pcall(require, 'nvim-treesitter')
local _, ts_highlight = pcall(require, 'nvim-treesitter.highlight')
local _, ts_parsers = pcall(require, 'nvim-treesitter.parsers')
local Job = require('plenary.job')
local utils = {}
utils.with_preview_window = function(status, bufnr, callable)
@@ -14,4 +20,50 @@ utils.with_preview_window = function(status, bufnr, callable)
end
end
-- API helper functions for buffer previewer
--- Job maker for buffer previewer
utils.job_maker = function(cmd, env, value, bufnr, bufname, callback)
if bufname ~= value then
local command = table.remove(cmd, 1)
Job:new({
command = command,
args = cmd,
env = env,
on_exit = vim.schedule_wrap(function(j)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, j:result())
if callback then callback(bufnr, j:result()) end
end)
}):start()
else
if callback then callback(bufnr) end
end
end
--- Attach default highlighter which will choose between regex and ts
utils.highlighter = function(bufnr, ft)
if ft and ft ~= '' then
if has_ts and ts_parsers.has_parser(ft) then
ts_highlight.attach(bufnr, ft)
else
vim.cmd(':ownsyntax ' .. ft)
end
end
end
--- Attach regex highlighter
utils.regex_highlighter = function(_, ft)
if ft and ft ~= '' then
vim.cmd(':ownsyntax ' .. ft)
end
end
-- Attach ts highlighter
utils.ts_highlighter = function(bufnr, ft)
if ft and ft ~= '' then
if has_ts and ts_parsers.has_parser(ft) then
ts_highlight.attach(bufnr, ft)
end
end
end
return utils

View File

@@ -35,8 +35,10 @@ highlight default link TelescopeResultsStruct Struct
highlight default link TelescopeResultsVariable SpecialChar
highlight default link TelescopeResultsLineNr LineNr
highlight default link TelescopeResultsIdentifier Identifier
highlight default link TelescopeResultsNumber Number
highlight default link TelescopeResultsComment Comment
highlight default link TelescopeResultsSpecialComment SpecialComment
" This is like "<C-R>" in your terminal.
" To use it, do `cmap <C-R> <Plug>(TelescopeFuzzyCommandSearch)