diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index a1806f37d4..3d1cd7a4d7 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -34,9 +34,18 @@ --- TODO: Document something we call `entry_index` ---@brief ]] +-- this breaks tree-sitter-lua docgen +-- ---@class telescope.entry +-- ---@field value any +-- ---@field ordinal string +-- ---@field display string|function(entry: telescope.entry): string +-- ---@field filename string|nil +-- ---@field bufnr number|nil +-- ---@field lnum number|nil +-- ---@field col number|nil + local entry_display = require "telescope.pickers.entry_display" local utils = require "telescope.utils" -local strings = require "plenary.strings" local Path = require "plenary.path" local treesitter_type_highlight = { @@ -151,24 +160,11 @@ do local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) - local disable_devicons = opts.disable_devicons - local mt_file_entry = {} mt_file_entry.cwd = cwd mt_file_entry.display = function(entry) - local hl_group, icon - local display, path_style = utils.transform_path(opts, entry.value) - - display, hl_group, icon = utils.transform_devicons(entry.value, display, disable_devicons) - - if hl_group then - local style = { { { 0, #icon + 1 }, hl_group } } - style = utils.merge_styles(style, path_style, #icon + 1) - return display, style - else - return display, path_style - end + return utils.create_path_display(entry, opts) end mt_file_entry.__index = function(t, k) @@ -272,8 +268,6 @@ do parse = parse_without_col end - local disable_devicons = opts.disable_devicons - local disable_coordinates = opts.disable_coordinates local only_sort_text = opts.only_sort_text local execute_keys = { @@ -309,38 +303,13 @@ do end end - local display_string = "%s%s%s" - mt_vimgrep_entry = { cwd = utils.path_expand(opts.cwd or vim.loop.cwd()), display = function(entry) - local display_filename, path_style = utils.transform_path(opts, entry.filename) - - local coordinates = ":" - if not disable_coordinates then - if entry.lnum then - if entry.col then - coordinates = string.format(":%s:%s:", entry.lnum, entry.col) - else - coordinates = string.format(":%s:", entry.lnum) - end - end - end - - local display, hl_group, icon = utils.transform_devicons( - entry.filename, - string.format(display_string, display_filename, coordinates, entry.text), - disable_devicons - ) - - if hl_group then - local style = { { { 0, #icon }, hl_group } } - style = utils.merge_styles(style, path_style, #icon + 1) - return display, style - else - return display, path_style - end + local display, path_style = utils.create_path_display(entry, opts) + display = string.format("%s:%s", display, entry.text) + return display, path_style end, __index = function(t, k) @@ -458,8 +427,7 @@ function make_entry.gen_from_quickfix(opts) local hidden = utils.is_path_hidden(opts) local make_display = function(entry) - local display_filename, path_style = utils.transform_path(opts, entry.filename) - local display_string = string.format("%s:%d:%d", display_filename, entry.lnum, entry.col) + local display_string, path_style = utils.create_path_display(entry, opts) if hidden then display_string = string.format("%4d:%2d", entry.lnum, entry.col) end @@ -540,7 +508,7 @@ function make_entry.gen_from_lsp_symbols(opts) msg, } else - local display_path, path_style = utils.transform_path(opts, entry.filename) + local display_path, path_style = utils.create_path_display(entry, opts) return displayer { { display_path, @@ -584,20 +552,11 @@ end function make_entry.gen_from_buffer(opts) opts = opts or {} - local disable_devicons = opts.disable_devicons - - local icon_width = 0 - if not disable_devicons then - local icon, _ = utils.get_devicons("fname", disable_devicons) - icon_width = strings.strdisplaywidth(icon) - end - local displayer = entry_display.create { separator = " ", items = { { width = opts.bufnr_width }, { width = 4 }, - { width = icon_width }, { remaining = true }, }, } @@ -605,19 +564,15 @@ function make_entry.gen_from_buffer(opts) local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) local make_display = function(entry) - -- bufnr_width + modes + icon + 3 spaces + : + lnum - opts.__prefix = opts.bufnr_width + 4 + icon_width + 3 + 1 + #tostring(entry.lnum) - local display_bufname, path_style = utils.transform_path(opts, entry.filename) - local icon, hl_group = utils.get_devicons(entry.filename, disable_devicons) + local display, style = utils.create_path_display(entry, opts) return displayer { { entry.bufnr, "TelescopeResultsNumber" }, { entry.indicator, "TelescopeResultsComment" }, - { icon, hl_group }, { - display_bufname .. ":" .. entry.lnum, + display, function() - return path_style + return style end, }, } @@ -1049,7 +1004,7 @@ function make_entry.gen_from_ctags(opts) } local make_display = function(entry) - local display_path, path_style = utils.transform_path(opts, entry.filename) + local display_path, path_style = utils.create_path_display(entry, opts) local scode if opts.show_line then @@ -1185,7 +1140,8 @@ function make_entry.gen_from_diagnostics(opts) } local make_display = function(entry) - local display_path, path_style = utils.transform_path(opts, entry.filename) + local display_path, path_style = + utils.create_path_display(entry, vim.tbl_extend("force", opts, { disable_coordinates = true })) -- add styling of entries local pos = string.format("%4d:%2d", entry.lnum, entry.col) @@ -1361,7 +1317,7 @@ function make_entry.gen_from_git_status(opts) local status_x = git_abbrev[x] or {} local status_y = git_abbrev[y] or {} - local display_path, path_style = utils.transform_path(opts, entry.path) + local display_path, path_style = utils.create_path_display(entry, opts) local empty_space = " " return displayer { diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index 44ab5d9b08..7cabe53682 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -18,10 +18,13 @@ local utils = {} utils.iswin = vim.loop.os_uname().sysname == "Windows_NT" --TODO(clason): Remove when dropping support for Nvim 0.9 +---@diagnostic disable-next-line: deprecated utils.islist = vim.fn.has "nvim-0.10" == 1 and vim.islist or vim.tbl_islist local flatten = function(t) return vim.iter(t):flatten():totable() end + +---@diagnostic disable-next-line: deprecated utils.flatten = vim.fn.has "nvim-0.11" == 1 and flatten or vim.tbl_flatten --- Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` @@ -170,9 +173,18 @@ utils.filter_symbols = function(results, opts, post_filter) end end -local path_filename_first = function(path, reverse_directories) - local dirs = vim.split(path, utils.get_separator()) +---@param path string +---@param reverse_directories boolean +---@param is_dir boolean +---@param dir_hl string? +---@param coordinates string +---@return string +---@return table +local path_filename_first = function(path, reverse_directories, is_dir, dir_hl, coordinates) + local sep = utils.get_separator() + local dirs = vim.split(path, sep) local filename + local path_style = {} if reverse_directories then dirs = utils.reverse_table(dirs) @@ -181,10 +193,15 @@ local path_filename_first = function(path, reverse_directories) filename = table.remove(dirs, #dirs) end - local tail = table.concat(dirs, utils.get_separator()) + if is_dir then + filename = filename .. sep + table.insert(path_style, { { 0, #filename }, dir_hl }) + end + + local tail = table.concat(dirs, sep) -- Trim prevents a top-level filename to have a trailing white space - local transformed_path = vim.trim(filename .. " " .. tail) - local path_style = { { { #filename, #transformed_path }, "TelescopeResultsComment" } } + local transformed_path = vim.trim(filename .. coordinates .. " " .. tail) + table.insert(path_style, { { #transformed_path - #tail, #transformed_path }, "TelescopeResultsComment" }) return transformed_path, path_style end @@ -333,88 +350,165 @@ utils.is_uri = function(filename) return false end +--- Normalize path_display table into a form +--- { [option] = { sub-option table } } +--- where `option` is one of "hidden"|"absolute"|"shorten"|"smart"|"truncate"|"filename_first +--- and `sub-option` is a table with the associated sub-options for a give +--- `option`. This can be an empty table. +---@param path_display table +---@return table +local function path_display_table_normalize(path_display) + local res = {} + for i, v in pairs(path_display) do + if type(i) == "number" then + res[v] = {} + elseif type(i) == "string" then + if i == "shorten" and type(v) == "number" then + res[i] = { len = v } + elseif type(v) == "boolean" and v then + res[i] = {} + else + res[i] = v + end + end + end + return res +end + --- Transform path is a util function that formats a path based on path_display --- found in `opts` or the default value from config. --- It is meant to be used in make_entry to have a uniform interface for --- builtins as well as extensions utilizing the same user configuration +--- +--- Optionally can concatenate line and column number coordinates to the path +--- string when they are provided. +--- For all path_display options besides `filename_first`, the coordinates are +--- appended to the end of the path. +--- For `filename_first`, the coordinates are appended to the end of the +--- filename, before the rest of the path. +--- eg. `utils.lua:387:24 lua/telescope` +--- --- Note: It is only supported inside `make_entry`/`make_display` the use of --- this function outside of telescope might yield to undefined behavior and will --- not be addressed by us ---@param opts table: The opts the users passed into the picker. Might contains a path_display key ---@param path string|nil: The path that should be formatted +---@param coordinates string|nil: Line and colunm numbers to be displayed (eg. ':395:86') ---@return string: path to be displayed ---@return table: The transformed path ready to be displayed with the styling -utils.transform_path = function(opts, path) +utils.transform_path = function(opts, path, coordinates) + coordinates = vim.F.if_nil(coordinates, "") + if path == nil then - return "", {} + return coordinates, {} end if utils.is_uri(path) then - return path, {} + return path .. coordinates, {} end ---@type fun(opts:table, path: string): string, table? local path_display = vim.F.if_nil(opts.path_display, require("telescope.config").values.path_display) - local transformed_path = path local path_style = {} if type(path_display) == "function" then local custom_transformed_path, custom_path_style = path_display(opts, transformed_path) - return custom_transformed_path, custom_path_style or path_style - elseif utils.is_path_hidden(nil, path_display) then - return "", path_style - elseif type(path_display) == "table" then - if vim.tbl_contains(path_display, "tail") or path_display.tail then - return utils.path_tail(transformed_path), path_style - end - - if not vim.tbl_contains(path_display, "absolute") and not path_display.absolute then - transformed_path = path_abs(transformed_path, opts) - end + return custom_transformed_path .. coordinates, custom_path_style or path_style + end - if vim.tbl_contains(path_display, "smart") or path_display.smart then - transformed_path = utils.path_smart(transformed_path) - end + if utils.is_path_hidden(opts, path_display) then + return coordinates, path_style + end - if vim.tbl_contains(path_display, "shorten") or path_display["shorten"] ~= nil then - local length - local exclude = nil + if type(path_display) ~= "table" then + log.warn("`path_display` must be either a function or a table.", "See `:help telescope.defaults.path_display.") + return transformed_path .. coordinates, path_style + end - if type(path_display["shorten"]) == "table" then - local shorten = path_display["shorten"] - length = shorten.len - exclude = shorten.exclude - else - length = type(path_display["shorten"]) == "number" and path_display["shorten"] - end + local display_opts = path_display_table_normalize(path_display) + local is_dir = opts.dir_hl and vim.fn.isdirectory(path) == 1 or false + local sep = utils.get_separator() - transformed_path = path_shorten(transformed_path, length, exclude) + if display_opts.tail then + transformed_path = utils.path_tail(transformed_path) + if is_dir then + transformed_path = transformed_path .. sep + table.insert(path_style, { { 0, #transformed_path }, opts.dir_hl }) end + return transformed_path .. coordinates, path_style + end - if vim.tbl_contains(path_display, "truncate") or path_display.truncate then - transformed_path = path_truncate(transformed_path, path_display.truncate, opts) - end + if not display_opts.absolute then + transformed_path = path_abs(transformed_path, opts) + end + if display_opts.smart then + transformed_path = utils.path_smart(transformed_path) + end + if display_opts.shorten then + transformed_path = path_shorten(transformed_path, display_opts.shorten.len, display_opts.shorten.exclude) + end + if display_opts.truncate then + transformed_path = path_truncate(transformed_path, display_opts.truncate, opts) + end + + if display_opts.filename_first then + return path_filename_first( + transformed_path, + display_opts.filename_first.reverse_directories, + is_dir, + opts.dir_hl, + coordinates + ) + end - if vim.tbl_contains(path_display, "filename_first") or path_display["filename_first"] ~= nil then - local reverse_directories = false + if is_dir then + transformed_path = transformed_path .. sep + table.insert(path_style, { { 0, #transformed_path }, opts.dir_hl }) + end - if type(path_display["filename_first"]) == "table" then - local filename_first_opts = path_display["filename_first"] + return transformed_path .. coordinates, path_style +end - if filename_first_opts.reverse_directories == nil or filename_first_opts.reverse_directories == false then - reverse_directories = false - else - reverse_directories = filename_first_opts.reverse_directories - end - end +-- this breaks tree-sitter-lua docgen +-- ---@class telescope.create_path_display.opts +-- ---@field path_display table|function(opts:table, path:string): string, table? +-- ---@field disable_devicons boolean? +-- ---@field disable_coordinates boolean? +-- ---@field dir_hl string? If set, the directory part of the path will be highlighted with this hl group - transformed_path, path_style = path_filename_first(transformed_path, reverse_directories) +--- Combines devicon, path and lnum/col into a single string and calculates hl +--- group and positions for a given entry record. +--- +--- Should be the preferred method to create a path display for an entry, semi-obsoleting +--- |telescope.utils.transform_path|. +---@param entry table: telescope.entry +---@param opts table: telescope.create_path_display.opts +---@return string diplay string with devicons, path and coordinates +---@return table style table with hl groups and positions +utils.create_path_display = function(entry, opts) + local path = require("telescope.from_entry").path(entry, false, false) + + local coordinates = "" + if not opts.disable_coordinates then + if entry.lnum then + if entry.col then + coordinates = string.format(":%s:%s", entry.lnum, entry.col) + else + coordinates = string.format(":%s", entry.lnum) + end end + end - return transformed_path, path_style + local hl_group, icon + local display, path_style = utils.transform_path(opts, path, coordinates) + display, hl_group, icon = utils.transform_devicons(path, display, opts.disable_devicons) + + if hl_group then + local style = { { { 0, #icon + 1 }, hl_group } } + style = utils.merge_styles(style, path_style, #icon + 1) + return display, style else - log.warn("`path_display` must be either a function or a table.", "See `:help telescope.defaults.path_display.") - return transformed_path, path_style + return display, path_style end end