From a6c7498bdcd661e86ec0c1181ed50024beb540f7 Mon Sep 17 00:00:00 2001 From: fdschmidt93 <39233597+fdschmidt93@users.noreply.github.com> Date: Mon, 27 Sep 2021 15:24:35 +0200 Subject: [PATCH] feat: filetype_hook & improved docs; fix preview partial override (#1273) --- doc/telescope.txt | 56 ++++++++++++-- lua/telescope/config.lua | 59 +++++++++++--- lua/telescope/previewers/buffer_previewer.lua | 77 ++++++++----------- lua/telescope/previewers/utils.lua | 45 ++++++++++- plugin/telescope.vim | 2 +- 5 files changed, 173 insertions(+), 66 deletions(-) diff --git a/doc/telescope.txt b/doc/telescope.txt index de019dd..039623e 100644 --- a/doc/telescope.txt +++ b/doc/telescope.txt @@ -315,14 +315,52 @@ telescope.setup({opts}) *telescope.setup()* complete within `timeout` milliseconds. Set to false to not timeout preview. Default: 250 - - hook(s): Function(s) that takes `(filepath, bufnr, opts)` - to be run if the buffer previewer was not shown due to - the respective test. - Available hooks are: {mime, filesize, timeout}_hook, e.g. - preview = { - mime_hook = function(filepath, bufnr, opts) ... end - } - See `telescope/previewers/*.lua` for relevant examples. + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: if plenary does not recognize your filetype yet -- + 1) Please consider contributing to: + $PLENARY_REPO/data/plenary/filetypes/builtin.lua + 2) Register your filetype locally as per link + https://github.com/nvim-lua/plenary.nvim#plenaryfiletype Default: nil - treesitter: Determines whether the previewer performs treesitter highlighting, which falls back to regex-based highlighting. @@ -331,6 +369,8 @@ telescope.setup({opts}) *telescope.setup()* `table`: table of filetypes for which to attach treesitter highlighting Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" *telescope.defaults.vimgrep_arguments* diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua index 37ca919..8f09c47 100644 --- a/lua/telescope/config.lua +++ b/lua/telescope/config.lua @@ -402,6 +402,7 @@ append( filesize_limit = 25, timeout = 250, treesitter = true, + msg_bg_fillchar = "╱", }, [[ This field handles the global configuration for previewers. @@ -426,14 +427,52 @@ append( complete within `timeout` milliseconds. Set to false to not timeout preview. Default: 250 - - hook(s): Function(s) that takes `(filepath, bufnr, opts)` - to be run if the buffer previewer was not shown due to - the respective test. - Available hooks are: {mime, filesize, timeout}_hook, e.g. - preview = { - mime_hook = function(filepath, bufnr, opts) ... end - } - See `telescope/previewers/*.lua` for relevant examples. + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: if plenary does not recognize your filetype yet -- + 1) Please consider contributing to: + $PLENARY_REPO/data/plenary/filetypes/builtin.lua + 2) Register your filetype locally as per link + https://github.com/nvim-lua/plenary.nvim#plenaryfiletype Default: nil - treesitter: Determines whether the previewer performs treesitter highlighting, which falls back to regex-based highlighting. @@ -442,6 +481,8 @@ append( `table`: table of filetypes for which to attach treesitter highlighting Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" ]] ) @@ -697,7 +738,7 @@ function config.set_defaults(user_defaults, tele_defaults) vim.tbl_deep_extend("keep", if_nil(config.values[name], {}), if_nil(default_val, {})) ) end - if name == "history" or name == "cache_picker" then + if name == "history" or name == "cache_picker" or name == "preview" then if user_defaults[name] == false or config.values[name] == false then return false end diff --git a/lua/telescope/previewers/buffer_previewer.lua b/lua/telescope/previewers/buffer_previewer.lua index 8ad21dd..feb95f0 100644 --- a/lua/telescope/previewers/buffer_previewer.lua +++ b/lua/telescope/previewers/buffer_previewer.lua @@ -1,7 +1,6 @@ local from_entry = require "telescope.from_entry" local Path = require "plenary.path" local utils = require "telescope.utils" -local strings = require "plenary.strings" local putils = require "telescope.previewers.utils" local Previewer = require "telescope.previewers.previewer" local conf = require("telescope.config").values @@ -56,39 +55,6 @@ local function defaulter(f, default_opts) } end -local function set_timeout_message(bufnr, winid, message) - local height = vim.api.nvim_win_get_height(winid) - local width = vim.api.nvim_win_get_width(winid) - vim.api.nvim_buf_set_lines( - bufnr, - 0, - -1, - false, - utils.repeated_table(height, table.concat(utils.repeated_table(width, "╱"), "")) - ) - for linenr = 0, height do - vim.api.nvim_buf_add_highlight(bufnr, -1, "TelescopePreviewMessage", linenr, 0, -1) - end - local anon_ns = vim.api.nvim_create_namespace "" - local padding = table.concat(utils.repeated_table(#message + 4, " "), "") - local lines = { - padding, - " " .. message .. " ", - padding, - } - - local col = math.floor((width - strings.strdisplaywidth(lines[2])) / 2) - for i, line in ipairs(lines) do - vim.api.nvim_buf_set_extmark( - bufnr, - anon_ns, - math.floor(height / 2) - 1 + i, - 0, - { virt_text = { { line, "TelescopePreviewMessage" } }, virt_text_pos = "overlay", virt_text_win_col = col } - ) - end -end - -- modified vim.split to incorporate a timer local function split(s, sep, plain, opts) opts = opts or {} @@ -190,14 +156,20 @@ previewers.file_maker = function(filepath, bufnr, opts) opts.preview = opts.preview or {} opts.preview.timeout = vim.F.if_nil(opts.preview.timeout, 250) -- in ms opts.preview.filesize_limit = vim.F.if_nil(opts.preview.filesize_limit, 25) -- in mb + opts.preview.msg_bg_fillchar = vim.F.if_nil(opts.preview.msg_bg_fillchar, "╱") -- in mb if opts.use_ft_detect == nil then opts.use_ft_detect = true end - local ft = opts.use_ft_detect and pfiletype.detect(filepath) + opts.ft = opts.use_ft_detect and pfiletype.detect(filepath) if opts.bufname ~= filepath then if not vim.in_fast_event() then filepath = vim.fn.expand(filepath) end + if type(opts.preview.filetype_hook) == "function" then + if not opts.preview.filetype_hook(filepath, bufnr, opts) then + return + end + end vim.loop.fs_stat(filepath, function(_, stat) if not stat then return @@ -215,17 +187,20 @@ previewers.file_maker = function(filepath, bufnr, opts) end), }) else - if opts.preview.check_mime_type == true and has_file and ft == "" then + if opts.preview.check_mime_type == true and has_file and opts.ft == "" then -- avoid SIGABRT in buffer previewer happening with utils.get_os_command_output local output = capture(string.format([[file --mime-type -b "%s"]], filepath)) local mime_type = vim.split(output, "/")[1] if mime_type ~= "text" and mime_type ~= "inode" then if type(opts.preview.mime_hook) == "function" then - opts.preview.mime_hook(filepath, bufnr, opts) + vim.schedule_wrap(opts.preview.mime_hook)(filepath, bufnr, opts) else - vim.schedule(function() - set_timeout_message(bufnr, opts.winid, "Binary cannot be previewed") - end) + vim.schedule_wrap(putils.set_preview_message)( + bufnr, + opts.winid, + "Binary cannot be previewed", + opts.preview.msg_bg_fillchar + ) end return end @@ -235,11 +210,14 @@ previewers.file_maker = function(filepath, bufnr, opts) local mb_filesize = math.floor(stat.size / bytes_to_megabytes) if mb_filesize > opts.preview.filesize_limit then if type(opts.preview.filesize_hook) == "function" then - opts.preview.filesize_hook(filepath, bufnr, opts) + vim.schedule_wrap(opts.preview.filesize_hook)(filepath, bufnr, opts) else - vim.schedule(function() - set_timeout_message(bufnr, opts.winid, "File exceeds preview size limit") - end) + vim.schedule_wrap(putils.set_preview_message)( + bufnr, + opts.winid, + "File exceeds preview size limit", + opts.preview.msg_bg_fillchar + ) end return end @@ -261,12 +239,17 @@ previewers.file_maker = function(filepath, bufnr, opts) if opts.callback then opts.callback(bufnr) end - putils.highlighter(bufnr, ft, opts) + putils.highlighter(bufnr, opts.ft, opts) else if type(opts.preview.timeout_hook) == "function" then - opts.preview.timeout_hook(filepath, bufnr, opts) + vim.schedule_wrap(opts.preview.timeout_hook)(filepath, bufnr, opts) else - set_timeout_message(bufnr, opts.winid, "Previewer timed out") + vim.schedule_wrap(putils.set_preview_message)( + bufnr, + opts.winid, + "Previewer timed out", + opts.preview.msg_bg_fillchar + ) end return end diff --git a/lua/telescope/previewers/utils.lua b/lua/telescope/previewers/utils.lua index 234eb0c..8acbd58 100644 --- a/lua/telescope/previewers/utils.lua +++ b/lua/telescope/previewers/utils.lua @@ -1,4 +1,7 @@ local context_manager = require "plenary.context_manager" +local ts_utils = require "telescope.utils" +local strings = require "plenary.strings" +local conf = require("telescope.config").values local has_ts, _ = pcall(require, "nvim-treesitter") local _, ts_configs = pcall(require, "nvim-treesitter.configs") @@ -66,7 +69,10 @@ end utils.highlighter = function(bufnr, ft, opts) opts = opts or {} opts.preview = opts.preview or {} - opts.preview.treesitter = vim.F.if_nil(opts.preview.treesitter, true) + opts.preview.treesitter = vim.F.if_nil( + opts.preview.treesitter, + type(conf.preview) == "table" and conf.preview.treesitter + ) local ts_highlighting = opts.preview.treesitter == true or type(opts.preview.treesitter) == "table" and vim.tbl_contains(opts.preview.treesitter, ft) @@ -127,4 +133,41 @@ utils.ts_highlighter = function(bufnr, ft) return false end +utils.set_preview_message = function(bufnr, winid, message, fillchar) + fillchar = vim.F.if_nil(fillchar, "╱") + local height = vim.api.nvim_win_get_height(winid) + local width = vim.api.nvim_win_get_width(winid) + vim.api.nvim_buf_set_lines( + bufnr, + 0, + -1, + false, + ts_utils.repeated_table(height, table.concat(ts_utils.repeated_table(width, fillchar), "")) + ) + local anon_ns = vim.api.nvim_create_namespace "" + local padding = table.concat(ts_utils.repeated_table(#message + 4, " "), "") + local lines = { + padding, + " " .. message .. " ", + padding, + } + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + 0, + 0, + { end_line = height, hl_group = "TelescopePreviewMessageFillchar" } + ) + local col = math.floor((width - strings.strdisplaywidth(lines[2])) / 2) + for i, line in ipairs(lines) do + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + math.floor(height / 2) - 1 + i, + 0, + { virt_text = { { line, "TelescopePreviewMessage" } }, virt_text_pos = "overlay", virt_text_win_col = col } + ) + end +end + return utils diff --git a/plugin/telescope.vim b/plugin/telescope.vim index e6ff508..e931b6c 100644 --- a/plugin/telescope.vim +++ b/plugin/telescope.vim @@ -53,8 +53,8 @@ highlight default link TelescopePreviewSize String highlight default link TelescopePreviewUser Constant highlight default link TelescopePreviewGroup Constant highlight default link TelescopePreviewDate Directory - highlight default link TelescopePreviewMessage TelescopePreviewNormal +highlight default link TelescopePreviewFillchar TelescopePreviewNormal " Used for Picker specific Results highlighting highlight default link TelescopeResultsClass Function