telescope.nvim
telescope.nvim copied to clipboard
Ability to make file name more visible in file path
Is your feature request related to a problem? Please describe. When I use git_files or find_files on large project with a lot of directories, it is sometimes a bit hard to spot the filename from the rest of the path.
Describe the solution you'd like I would really love to have a built-in option to bolden/put a colot emphasis on the file name in the results so I can quickly spot out the file name.
Describe alternatives you've considered I've considered
-- Format path as "file.txt (path\to\file\)"
path_display = function(opts, path)
local tail = require("telescope.utils").path_tail(path)
return string.format("%s (%s)", tail, path)
end,
But I am not sure how to handle coloring/bolding output in lua, I am a bit unfamiliar with the language.
Additional context I would love to open a PR for this if you would have this feature, I may need a bit of help to get started though.
Thanks for the awesome work you guys have been doing !
path_display is currently not integrated with our hightlighter. Shouldn't be hard to do, will add it later.
After that you should be able to do
-- Format path as "file.txt (path\to\file\)"
path_display = function(opts, path)
local tail = require("telescope.utils").path_tail(path)
return string.format("%s (%s)", tail, path), { { { 1, #tail }, "Constant" } }
end,
I too require this feature implemented. Thanks
Yes please. I would love this too!
Bumping +1 ;)
This feature would be very nice to have indeed!
I quite like the formatting of the file picker in VSCode where the path of the file is darker:
But in Telescope, the file name (first section before the dash) is not very easy to discern from the full file path:
agreed, this would be ace!
nice. care to share the config snipet @towry ?
I tried adding highlights for tail / path / 'constant' but wasnt sure if I was doing something wrong (highly likely) or the feature hasn't been implemented yet as the issue is still open and no other hint that it's been implemented after Connies comment.
@tjex Here it is 😁
The main part is lines between comment --- //////// item stylish., you just need copy that part into your own config, dependencies required: { 'nvim-web-devicons', 'telescope', 'plenary', 'M.get_path_and_tail' }
local M = {}
M.get_path_and_tail = function(filename)
local utils = require('telescope.utils')
local bufname_tail = utils.path_tail(filename)
local path_without_tail = require('plenary.strings').truncate(filename, #filename - #bufname_tail, '')
local path_to_display = utils.transform_path({
path_display = { 'truncate' },
}, path_without_tail)
return bufname_tail, path_to_display
end
local use_find_files_instead_of_git = true
M.project_files_toggle_between_git_and_fd = function()
use_find_files_instead_of_git = not use_find_files_instead_of_git
end
M.project_files = function(opts)
local make_entry = require('telescope.make_entry')
local strings = require('plenary.strings')
local utils = require('telescope.utils')
local entry_display = require('telescope.pickers.entry_display')
local devicons = require('nvim-web-devicons')
local def_icon = devicons.get_icon('fname', { default = true })
local iconwidth = strings.strdisplaywidth(def_icon)
local level_up = vim.v.count
local map_i_actions = function(prompt_bufnr, map)
map('i', '<C-o>', function()
require('libs.telescope.picker_keymaps').open_selected_in_window(prompt_bufnr)
end, { noremap = true, silent = true })
end
opts = opts or {}
if not opts.cwd then
opts.cwd = require('libs.telescope.utils').get_cwd_relative_to_buf(0, level_up)
end
opts.hidden = true
local nicely_cwd = require('libs.runtime.path').home_to_tilde(opts.cwd)
opts.prompt_title = opts.prompt_title or nicely_cwd
opts.attach_mappings = function(_, map)
map_i_actions(_, map)
return true
end
--- //////// item stylish.
local entry_make = make_entry.gen_from_file(opts)
opts.entry_maker = function(line)
local entry = entry_make(line)
local displayer = entry_display.create({
separator = ' ',
items = {
{ width = iconwidth },
{ width = nil },
{ remaining = true },
},
})
entry.display = function(et)
-- https://github.com/nvim-telescope/telescope.nvim/blob/master/lua/telescope/make_entry.lua
local tail_raw, path_to_display = M.get_path_and_tail(et.value)
local tail = tail_raw .. ' '
local icon, iconhl = utils.get_devicons(tail_raw)
return displayer({
{ icon, iconhl },
tail,
{ path_to_display, 'TelescopeResultsComment' },
})
end
return entry
end
---/// end item stylish
if opts and opts.oldfiles then
local cache_opts = vim.tbl_deep_extend('force', {
}, opts)
local cycle = require('libs.telescope.cycle')(
function(income_opts)
require('telescope.builtin').find_files(vim.tbl_extend('force', cache_opts, {
results_title = ' All Files: ',
}, income_opts))
end
)
opts = vim.tbl_extend('force', {
results_title = ' Recent files: ',
prompt_title = ' Recent files',
attach_mappings = function(_, map)
map_i_actions(_, map)
map('i', '<C-b>', cycle.next, { noremap = true, silent = true })
return true
end
}, opts)
return require('telescope.builtin').oldfiles(opts)
end
-- use find_files or git_files.
local use_all_files = opts.use_all_files or false
if (opts and opts.no_gitfiles) or use_find_files_instead_of_git then
use_all_files = true
end
local ok = (function()
if use_all_files then return false end
opts.results_title = ' Git Files: '
local is_git_ok = pcall(require('telescope.builtin').git_files, opts)
return is_git_ok
end)()
if not ok then
opts.results_title = ' All Files: '
require('telescope.builtin').find_files(opts)
end
end
function M.buffers_or_recent()
local count = #vim.fn.getbufinfo({ buflisted = 1 })
if count <= 1 then
--- open recent.
M.project_files({
cwd_only = true,
oldfiles = true,
})
return
end
return M.buffers()
end
function M.buffers()
local builtin = require('telescope.builtin')
local actions = require('telescope.actions')
local actionstate = require('telescope.actions.state')
local Buffer = require('libs.runtime.buffer')
builtin.buffers({
ignore_current_buffer = true,
sort_mru = true,
-- layout_strategy = 'vertical',
layout_strategy = "bottom_pane",
entry_maker = M.gen_from_buffer({
bufnr_width = 2,
sort_mru = true,
}),
attach_mappings = function(prompt_bufnr, map)
local close_buf = function()
-- local picker = actionstate.get_current_picker(prompt_bufnr)
local selection = actionstate.get_selected_entry()
actions.close(prompt_bufnr)
vim.api.nvim_buf_delete(selection.bufnr, { force = false })
local state = require('telescope.state')
local cached_pickers = state.get_global_key('cached_pickers') or {}
-- remove this picker cache
table.remove(cached_pickers, 1)
end
local open_selected = function()
local entry = actionstate.get_selected_entry()
actions.close(prompt_bufnr)
if not entry or (not entry.bufnr) then
vim.notify("no selected entry found")
return
end
local bufnr = entry.bufnr
Buffer.set_current_buffer_focus(bufnr)
end
map('i', '<C-h>', close_buf)
map('i', '<CR>', open_selected)
return true
end,
})
end
function M.gen_from_buffer(opts)
local runtimeUtils = require('libs.runtime.utils')
local utils = require('telescope.utils')
local strings = require('plenary.strings')
local entry_display = require('telescope.pickers.entry_display')
local Path = require('plenary.path')
local make_entry = require('telescope.make_entry')
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 cwd = vim.fn.expand(opts.cwd or runtimeUtils.get_root() or ".")
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 bufname_tail = utils.path_tail(entry.filename)
local path_without_tail = require('plenary.strings').truncate(entry.filename, #entry.filename - #bufname_tail, '')
local path_to_display = utils.transform_path({
path_display = { 'truncate' },
}, path_without_tail)
local bufname_width = strings.strdisplaywidth(bufname_tail)
local icon, hl_group = utils.get_devicons(entry.filename, disable_devicons)
local displayer = entry_display.create({
separator = ' ',
items = {
{ width = opts.bufnr_width },
{ width = 4 },
{ width = icon_width },
{ width = bufname_width },
{ remaining = true },
},
})
return displayer({
{ entry.bufnr, 'TelescopeResultsNumber' },
{ entry.indicator, 'TelescopeResultsComment' },
{ icon, hl_group },
bufname_tail,
{ path_to_display .. ':' .. entry.lnum, 'TelescopeResultsComment' },
})
end
return function(entry)
local bufname = entry.info.name ~= '' and entry.info.name or '[No Name]'
-- if bufname is inside the cwd, trim that part of the string
bufname = Path:new(bufname):normalize(cwd)
local hidden = entry.info.hidden == 1 and 'h' or 'a'
-- local readonly = vim.api.nvim_buf_get_option(entry.bufnr, 'readonly') and '=' or ' '
local readonly = vim.api.nvim_get_option_value('readonly', {
buf = entry.bufnr,
}) and '=' or ' '
local changed = entry.info.changed == 1 and '+' or ' '
local indicator = entry.flag .. hidden .. readonly .. changed
local lnum = 1
-- account for potentially stale lnum as getbufinfo might not be updated or from resuming buffers picker
if entry.info.lnum ~= 0 then
-- but make sure the buffer is loaded, otherwise line_count is 0
if vim.api.nvim_buf_is_loaded(entry.bufnr) then
local line_count = vim.api.nvim_buf_line_count(entry.bufnr)
lnum = math.max(math.min(entry.info.lnum, line_count), 1)
else
lnum = entry.info.lnum
end
end
return make_entry.set_default_entry_mt({
value = bufname,
ordinal = entry.bufnr .. ' : ' .. bufname,
display = make_display,
bufnr = entry.bufnr,
filename = bufname,
lnum = lnum,
indicator = indicator,
}, opts)
end
end
return M
omg I'd like to use this in all my pickers. the current default causes too much overload for my brain. @towry this looks fantastic!
@towry Any chance you can post the code snippet here? I'm getting a 404 on clicking the link (repo is taken private?).
@SandeepTuniki the snippet is there. https://github.com/nvim-telescope/telescope.nvim/issues/2014#issuecomment-1541423345
@SandeepTuniki the snippet is there. https://github.com/nvim-telescope/telescope.nvim/issues/2014#issuecomment-1541423345
Ah yes. Looks like the comment was updated from a link to the code snippet (thanks, @towry!)
Thank you @towry for that great snippet, it inspired me to create custom Pickers for both File finders and Grep searchers. I'll leave it here if anyone wants to change their Pickers appearance easily, simply create a lua module file inside of your nvim/lua folder called telescopePickers and replace ggv.telescopePickers with your own module location.
Available pickers are:
live_grep
require('ggv.telescopePickers').prettyGrepPicker({ picker = 'live_grep' })
grep_string
require('ggv.telescopePickers').prettyGrepPicker({ picker = 'grep_string' })
git_files
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'git_files' })
find_files
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'find_files' })
oldfiles
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'oldfiles' })
telescopePickers.lua
-- Declare the module
local telescopePickers = {}
-- Store Utilities we'll use frequently
local telescopeUtilities = require('telescope.utils')
local telescopeMakeEntryModule = require('telescope.make_entry')
local plenaryStrings = require('plenary.strings')
local devIcons = require('nvim-web-devicons')
local telescopeEntryDisplayModule = require('telescope.pickers.entry_display')
-- Obtain Filename icon width
-- --------------------------
-- INSIGHT: This width applies to all icons that represent a file type
local fileTypeIconWidth = plenaryStrings.strdisplaywidth(devIcons.get_icon('fname', { default = true }))
---- Helper functions ----
-- Gets the File Path and its Tail (the file name) as a Tuple
function telescopePickers.getPathAndTail(fileName)
-- Get the Tail
local bufferNameTail = telescopeUtilities.path_tail(fileName)
-- Now remove the tail from the Full Path
local pathWithoutTail = require('plenary.strings').truncate(fileName, #fileName - #bufferNameTail, '')
-- Apply truncation and other pertaining modifications to the path according to Telescope path rules
local pathToDisplay = telescopeUtilities.transform_path({
path_display = { 'truncate' },
}, pathWithoutTail)
-- Return as Tuple
return bufferNameTail, pathToDisplay
end
---- Picker functions ----
-- Generates a Find File picker but beautified
-- -------------------------------------------
-- This is a wrapping function used to modify the appearance of pickers that provide a Find File
-- functionality, mainly because the default one doesn't look good. It does this by changing the 'display()'
-- function that Telescope uses to display each entry in the Picker.
--
-- Adapted from: https://github.com/nvim-telescope/telescope.nvim/issues/2014#issuecomment-1541423345.
--
-- @param (table) pickerAndOptions - A table with the following format:
-- {
-- picker = '<pickerName>',
-- (optional) options = { ... }
-- }
function telescopePickers.prettyFilesPicker(pickerAndOptions)
-- Parameter integrity check
if type(pickerAndOptions) ~= 'table' or pickerAndOptions.picker == nil then
print("Incorrect argument format. Correct format is: { picker = 'desiredPicker', (optional) options = { ... } }")
-- Avoid further computation
return
end
-- Ensure 'options' integrity
options = pickerAndOptions.options or {}
-- Use Telescope's existing function to obtain a default 'entry_maker' function
-- ----------------------------------------------------------------------------
-- INSIGHT: Because calling this function effectively returns an 'entry_maker' function that is ready to
-- handle entry creation, we can later call it to obtain the final entry table, which will
-- ultimately be used by Telescope to display the entry by executing its 'display' key function.
-- This reduces our work by only having to replace the 'display' function in said table instead
-- of having to manipulate the rest of the data too.
local originalEntryMaker = telescopeMakeEntryModule.gen_from_file(options)
-- INSIGHT: 'entry_maker' is the hardcoded name of the option Telescope reads to obtain the function that
-- will generate each entry.
-- INSIGHT: The paramenter 'line' is the actual data to be displayed by the picker, however, its form is
-- raw (type 'any) and must be transformed into an entry table.
options.entry_maker = function(line)
-- Generate the Original Entry table
local originalEntryTable = originalEntryMaker(line)
-- INSIGHT: An "entry display" is an abstract concept that defines the "container" within which data
-- will be displayed inside the picker, this means that we must define options that define
-- its dimensions, like, for example, its width.
local displayer = telescopeEntryDisplayModule.create({
separator = ' ', -- Telescope will use this separator between each entry item
items = {
{ width = fileTypeIconWidth },
{ width = nil },
{ remaining = true },
},
})
-- LIFECYCLE: At this point the "displayer" has been created by the create() method, which has in turn
-- returned a function. This means that we can now call said function by using the
-- 'displayer' variable and pass it actual entry values so that it will, in turn, output
-- the entry for display.
--
-- INSIGHT: We now have to replace the 'display' key in the original entry table to modify the way it
-- is displayed.
-- INSIGHT: The 'entry' is the same Original Entry Table but is is passed to the 'display()' function
-- later on the program execution, most likely when the actual display is made, which could
-- be deferred to allow lazy loading.
--
-- HELP: Read the 'make_entry.lua' file for more info on how all of this works
originalEntryTable.display = function(entry)
-- Get the Tail and the Path to display
local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.value)
-- Add an extra space to the tail so that it looks nicely separated from the path
local tailForDisplay = tail .. ' '
-- Get the Icon with its corresponding Highlight information
local icon, iconHighlight = telescopeUtilities.get_devicons(tail)
-- INSIGHT: This return value should be a tuple of 2, where the first value is the actual value
-- and the second one is the highlight information, this will be done by the displayer
-- internally and return in the correct format.
return displayer({
{ icon, iconHighlight },
tailForDisplay,
{ pathToDisplay, 'TelescopeResultsComment' },
})
end
return originalEntryTable
end
-- Finally, check which file picker was requested and open it with its associated options
if pickerAndOptions.picker == 'find_files' then
require('telescope.builtin').find_files(options)
elseif pickerAndOptions.picker == 'git_files' then
require('telescope.builtin').git_files(options)
elseif pickerAndOptions.picker == 'oldfiles' then
require('telescope.builtin').oldfiles(options)
elseif pickerAndOptions.picker == '' then
print("Picker was not specified")
else
print("Picker is not supported by Pretty Find Files")
end
end
-- Generates a Grep Search picker but beautified
-- ----------------------------------------------
-- This is a wrapping function used to modify the appearance of pickers that provide Grep Search
-- functionality, mainly because the default one doesn't look good. It does this by changing the 'display()'
-- function that Telescope uses to display each entry in the Picker.
--
-- @param (table) pickerAndOptions - A table with the following format:
-- {
-- picker = '<pickerName>',
-- (optional) options = { ... }
-- }
function telescopePickers.prettyGrepPicker(pickerAndOptions)
-- Parameter integrity check
if type(pickerAndOptions) ~= 'table' or pickerAndOptions.picker == nil then
print("Incorrect argument format. Correct format is: { picker = 'desiredPicker', (optional) options = { ... } }")
-- Avoid further computation
return
end
-- Ensure 'options' integrity
options = pickerAndOptions.options or {}
-- Use Telescope's existing function to obtain a default 'entry_maker' function
-- ----------------------------------------------------------------------------
-- INSIGHT: Because calling this function effectively returns an 'entry_maker' function that is ready to
-- handle entry creation, we can later call it to obtain the final entry table, which will
-- ultimately be used by Telescope to display the entry by executing its 'display' key function.
-- This reduces our work by only having to replace the 'display' function in said table instead
-- of having to manipulate the rest of the data too.
local originalEntryMaker = telescopeMakeEntryModule.gen_from_vimgrep(options)
-- INSIGHT: 'entry_maker' is the hardcoded name of the option Telescope reads to obtain the function that
-- will generate each entry.
-- INSIGHT: The paramenter 'line' is the actual data to be displayed by the picker, however, its form is
-- raw (type 'any) and must be transformed into an entry table.
options.entry_maker = function(line)
-- Generate the Original Entry table
local originalEntryTable = originalEntryMaker(line)
-- INSIGHT: An "entry display" is an abstract concept that defines the "container" within which data
-- will be displayed inside the picker, this means that we must define options that define
-- its dimensions, like, for example, its width.
local displayer = telescopeEntryDisplayModule.create({
separator = ' ', -- Telescope will use this separator between each entry item
items = {
{ width = fileTypeIconWidth },
{ width = nil },
{ width = nil }, -- Maximum path size, keep it short
{ remaining = true },
},
})
-- LIFECYCLE: At this point the "displayer" has been created by the create() method, which has in turn
-- returned a function. This means that we can now call said function by using the
-- 'displayer' variable and pass it actual entry values so that it will, in turn, output
-- the entry for display.
--
-- INSIGHT: We now have to replace the 'display' key in the original entry table to modify the way it
-- is displayed.
-- INSIGHT: The 'entry' is the same Original Entry Table but is is passed to the 'display()' function
-- later on the program execution, most likely when the actual display is made, which could
-- be deferred to allow lazy loading.
--
-- HELP: Read the 'make_entry.lua' file for more info on how all of this works
originalEntryTable.display = function(entry)
---- Get File columns data ----
-------------------------------
-- Get the Tail and the Path to display
local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.filename)
-- Get the Icon with its corresponding Highlight information
local icon, iconHighlight = telescopeUtilities.get_devicons(tail)
---- Format Text for display ----
---------------------------------
-- Add coordinates if required by 'options'
local coordinates = ""
if not options.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
-- Append coordinates to tail
tail = tail .. coordinates
-- Add an extra space to the tail so that it looks nicely separated from the path
local tailForDisplay = tail .. ' '
-- Encode text if necessary
local text = options.file_encoding and vim.iconv(entry.text, options.file_encoding, "utf8") or entry.text
-- INSIGHT: This return value should be a tuple of 2, where the first value is the actual value
-- and the second one is the highlight information, this will be done by the displayer
-- internally and return in the correct format.
return displayer({
{ icon, iconHighlight },
tailForDisplay,
{ pathToDisplay, 'TelescopeResultsComment' },
text
})
end
return originalEntryTable
end
-- Finally, check which file picker was requested and open it with its associated options
if pickerAndOptions.picker == 'live_grep' then
require('telescope.builtin').live_grep(options)
elseif pickerAndOptions.picker == 'grep_string' then
require('telescope.builtin').grep_string(options)
elseif pickerAndOptions.picker == '' then
print("Picker was not specified")
else
print("Picker is not supported by Pretty Grep Picker")
end
end
-- Return the module for use
return telescopePickers
Thank you @towry for that great snippet, it inspired me to create custom Pickers for both File finders and Grep searchers. I'll leave it here if anyone wants to change their Pickers appearance easily, simply create a lua module file inside of your
nvim/luafolder calledtelescopePickersand replaceggv.telescopePickerswith your own module location.
![]()
Available pickers are:
live_grep
require('ggv.telescopePickers').prettyGrepPicker({ picker = 'live_grep' })grep_string
require('ggv.telescopePickers').prettyGrepPicker({ picker = 'grep_string' })git_files
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'git_files' })find_files
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'find_files' })oldfiles
require('ggv.telescopePickers').prettyFilesPicker({ picker = 'oldfiles' })telescopePickers.lua
-- Declare the module local telescopePickers = {} -- Store Utilities we'll use frequently local telescopeUtilities = require('telescope.utils') local telescopeMakeEntryModule = require('telescope.make_entry') local plenaryStrings = require('plenary.strings') local devIcons = require('nvim-web-devicons') local telescopeEntryDisplayModule = require('telescope.pickers.entry_display') -- Obtain Filename icon width -- -------------------------- -- INSIGHT: This width applies to all icons that represent a file type local fileTypeIconWidth = plenaryStrings.strdisplaywidth(devIcons.get_icon('fname', { default = true })) ---- Helper functions ---- -- Gets the File Path and its Tail (the file name) as a Tuple function telescopePickers.getPathAndTail(fileName) -- Get the Tail local bufferNameTail = telescopeUtilities.path_tail(fileName) -- Now remove the tail from the Full Path local pathWithoutTail = require('plenary.strings').truncate(fileName, #fileName - #bufferNameTail, '') -- Apply truncation and other pertaining modifications to the path according to Telescope path rules local pathToDisplay = telescopeUtilities.transform_path({ path_display = { 'truncate' }, }, pathWithoutTail) -- Return as Tuple return bufferNameTail, pathToDisplay end ---- Picker functions ---- -- Generates a Find File picker but beautified -- ------------------------------------------- -- This is a wrapping function used to modify the appearance of pickers that provide a Find File -- functionality, mainly because the default one doesn't look good. It does this by changing the 'display()' -- function that Telescope uses to display each entry in the Picker. -- -- Adapted from: https://github.com/nvim-telescope/telescope.nvim/issues/2014#issuecomment-1541423345. -- -- @param (table) pickerAndOptions - A table with the following format: -- { -- picker = '<pickerName>', -- (optional) options = { ... } -- } function telescopePickers.prettyFilesPicker(pickerAndOptions) -- Parameter integrity check if type(pickerAndOptions) ~= 'table' or pickerAndOptions.picker == nil then print("Incorrect argument format. Correct format is: { picker = 'desiredPicker', (optional) options = { ... } }") -- Avoid further computation return end -- Ensure 'options' integrity options = pickerAndOptions.options or {} -- Use Telescope's existing function to obtain a default 'entry_maker' function -- ---------------------------------------------------------------------------- -- INSIGHT: Because calling this function effectively returns an 'entry_maker' function that is ready to -- handle entry creation, we can later call it to obtain the final entry table, which will -- ultimately be used by Telescope to display the entry by executing its 'display' key function. -- This reduces our work by only having to replace the 'display' function in said table instead -- of having to manipulate the rest of the data too. local originalEntryMaker = telescopeMakeEntryModule.gen_from_file(options) -- INSIGHT: 'entry_maker' is the hardcoded name of the option Telescope reads to obtain the function that -- will generate each entry. -- INSIGHT: The paramenter 'line' is the actual data to be displayed by the picker, however, its form is -- raw (type 'any) and must be transformed into an entry table. options.entry_maker = function(line) -- Generate the Original Entry table local originalEntryTable = originalEntryMaker(line) -- INSIGHT: An "entry display" is an abstract concept that defines the "container" within which data -- will be displayed inside the picker, this means that we must define options that define -- its dimensions, like, for example, its width. local displayer = telescopeEntryDisplayModule.create({ separator = ' ', -- Telescope will use this separator between each entry item items = { { width = fileTypeIconWidth }, { width = nil }, { remaining = true }, }, }) -- LIFECYCLE: At this point the "displayer" has been created by the create() method, which has in turn -- returned a function. This means that we can now call said function by using the -- 'displayer' variable and pass it actual entry values so that it will, in turn, output -- the entry for display. -- -- INSIGHT: We now have to replace the 'display' key in the original entry table to modify the way it -- is displayed. -- INSIGHT: The 'entry' is the same Original Entry Table but is is passed to the 'display()' function -- later on the program execution, most likely when the actual display is made, which could -- be deferred to allow lazy loading. -- -- HELP: Read the 'make_entry.lua' file for more info on how all of this works originalEntryTable.display = function(entry) -- Get the Tail and the Path to display local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.value) -- Add an extra space to the tail so that it looks nicely separated from the path local tailForDisplay = tail .. ' ' -- Get the Icon with its corresponding Highlight information local icon, iconHighlight = telescopeUtilities.get_devicons(tail) -- INSIGHT: This return value should be a tuple of 2, where the first value is the actual value -- and the second one is the highlight information, this will be done by the displayer -- internally and return in the correct format. return displayer({ { icon, iconHighlight }, tailForDisplay, { pathToDisplay, 'TelescopeResultsComment' }, }) end return originalEntryTable end -- Finally, check which file picker was requested and open it with its associated options if pickerAndOptions.picker == 'find_files' then require('telescope.builtin').find_files(options) elseif pickerAndOptions.picker == 'git_files' then require('telescope.builtin').git_files(options) elseif pickerAndOptions.picker == 'oldfiles' then require('telescope.builtin').oldfiles(options) elseif pickerAndOptions.picker == '' then print("Picker was not specified") else print("Picker is not supported by Pretty Find Files") end end -- Generates a Grep Search picker but beautified -- ---------------------------------------------- -- This is a wrapping function used to modify the appearance of pickers that provide Grep Search -- functionality, mainly because the default one doesn't look good. It does this by changing the 'display()' -- function that Telescope uses to display each entry in the Picker. -- -- @param (table) pickerAndOptions - A table with the following format: -- { -- picker = '<pickerName>', -- (optional) options = { ... } -- } function telescopePickers.prettyGrepPicker(pickerAndOptions) -- Parameter integrity check if type(pickerAndOptions) ~= 'table' or pickerAndOptions.picker == nil then print("Incorrect argument format. Correct format is: { picker = 'desiredPicker', (optional) options = { ... } }") -- Avoid further computation return end -- Ensure 'options' integrity options = pickerAndOptions.options or {} -- Use Telescope's existing function to obtain a default 'entry_maker' function -- ---------------------------------------------------------------------------- -- INSIGHT: Because calling this function effectively returns an 'entry_maker' function that is ready to -- handle entry creation, we can later call it to obtain the final entry table, which will -- ultimately be used by Telescope to display the entry by executing its 'display' key function. -- This reduces our work by only having to replace the 'display' function in said table instead -- of having to manipulate the rest of the data too. local originalEntryMaker = telescopeMakeEntryModule.gen_from_vimgrep(options) -- INSIGHT: 'entry_maker' is the hardcoded name of the option Telescope reads to obtain the function that -- will generate each entry. -- INSIGHT: The paramenter 'line' is the actual data to be displayed by the picker, however, its form is -- raw (type 'any) and must be transformed into an entry table. options.entry_maker = function(line) -- Generate the Original Entry table local originalEntryTable = originalEntryMaker(line) -- INSIGHT: An "entry display" is an abstract concept that defines the "container" within which data -- will be displayed inside the picker, this means that we must define options that define -- its dimensions, like, for example, its width. local displayer = telescopeEntryDisplayModule.create({ separator = ' ', -- Telescope will use this separator between each entry item items = { { width = fileTypeIconWidth }, { width = nil }, { width = nil }, -- Maximum path size, keep it short { remaining = true }, }, }) -- LIFECYCLE: At this point the "displayer" has been created by the create() method, which has in turn -- returned a function. This means that we can now call said function by using the -- 'displayer' variable and pass it actual entry values so that it will, in turn, output -- the entry for display. -- -- INSIGHT: We now have to replace the 'display' key in the original entry table to modify the way it -- is displayed. -- INSIGHT: The 'entry' is the same Original Entry Table but is is passed to the 'display()' function -- later on the program execution, most likely when the actual display is made, which could -- be deferred to allow lazy loading. -- -- HELP: Read the 'make_entry.lua' file for more info on how all of this works originalEntryTable.display = function(entry) ---- Get File columns data ---- ------------------------------- -- Get the Tail and the Path to display local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.filename) -- Get the Icon with its corresponding Highlight information local icon, iconHighlight = telescopeUtilities.get_devicons(tail) ---- Format Text for display ---- --------------------------------- -- Add coordinates if required by 'options' local coordinates = "" if not options.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 -- Append coordinates to tail tail = tail .. coordinates -- Add an extra space to the tail so that it looks nicely separated from the path local tailForDisplay = tail .. ' ' -- Encode text if necessary local text = options.file_encoding and vim.iconv(entry.text, options.file_encoding, "utf8") or entry.text -- INSIGHT: This return value should be a tuple of 2, where the first value is the actual value -- and the second one is the highlight information, this will be done by the displayer -- internally and return in the correct format. return displayer({ { icon, iconHighlight }, tailForDisplay, { pathToDisplay, 'TelescopeResultsComment' }, text }) end return originalEntryTable end -- Finally, check which file picker was requested and open it with its associated options if pickerAndOptions.picker == 'live_grep' then require('telescope.builtin').live_grep(options) elseif pickerAndOptions.picker == 'grep_string' then require('telescope.builtin').grep_string(options) elseif pickerAndOptions.picker == '' then print("Picker was not specified") else print("Picker is not supported by Pretty Grep Picker") end end -- Return the module for use return telescopePickers
Awesome, will be default for https://github.com/otavioschwanck/mood-nvim
@towry do you have a buffers picker too?
Here's an addition to @towry's for lsp_document_symbols, lsp_dynamic_workspace_symbols, and buffers.
Buffers:
Document symbols:
Workspace symbols:
Can be used like so:
vim.keymap.set('n', '<leader>fb', function() pickers.prettyBuffersPicker() end, opts)
vim.keymap.set('n', '<leader>fs', function() pickers.prettyDocumentSymbols() end, opts)
vim.keymap.set('n', '<leader>ws', function() pickers.prettyWorkspaceSymbols() end, opts)
Append the below snippet to your telescopePickers.lua file that towry created (before the return statement). Note that this uses a local kind_icons array for the lsp symbols, but if you don't want them, you can render them some other way.
local kind_icons = {
Text = "",
String = "",
Array = "",
Object = "",
Namespace = "",
Method = "m",
Function = "",
Constructor = "",
Field = "",
Variable = "",
Class = "",
Interface = "",
Module = "",
Property = "",
Unit = "",
Value = "",
Enum = "",
Keyword = "",
Snippet = "",
Color = "",
File = "",
Reference = "",
Folder = "",
EnumMember = "",
Constant = "",
Struct = "",
Event = "",
Operator = "",
TypeParameter = "",
Copilot = "🤖",
Boolean = "",
}
function telescopePickers.prettyDocumentSymbols(localOptions)
if localOptions ~= nil and type(localOptions) ~= 'table' then
print("Options must be a table.")
return
end
options = localOptions or {}
local originalEntryMaker = telescopeMakeEntryModule.gen_from_lsp_symbols(options)
options.entry_maker = function(line)
local originalEntryTable = originalEntryMaker(line)
local displayer = telescopeEntryDisplayModule.create({
separator = ' ',
items = {
{ width = fileTypeIconWidth },
{ width = 20 },
{ remaining = true },
},
})
originalEntryTable.display = function(entry)
return displayer {
string.format("%s", kind_icons[(entry.symbol_type:lower():gsub("^%l", string.upper))]),
{ entry.symbol_type:lower(), 'TelescopeResultsVariable' },
{ entry.symbol_name, 'TelescopeResultsConstant' },
}
end
return originalEntryTable
end
require('telescope.builtin').lsp_document_symbols(options)
end
function telescopePickers.prettyWorkspaceSymbols(localOptions)
if localOptions ~= nil and type(localOptions) ~= 'table' then
print("Options must be a table.")
return
end
options = localOptions or {}
local originalEntryMaker = telescopeMakeEntryModule.gen_from_lsp_symbols(options)
options.entry_maker = function(line)
local originalEntryTable = originalEntryMaker(line)
local displayer = telescopeEntryDisplayModule.create({
separator = ' ',
items = {
{ width = fileTypeIconWidth },
{ width = 15 },
{ width = 30 },
{ width = nil },
{ remaining = true },
},
})
originalEntryTable.display = function(entry)
local tail, _ = telescopePickers.getPathAndTail(entry.filename)
local tailForDisplay = tail .. ' '
local pathToDisplay = telescopeUtilities.transform_path({
path_display = { shorten = { num = 2, exclude = {-2, -1} }, 'truncate' },
}, entry.value.filename)
return displayer {
string.format("%s", kind_icons[(entry.symbol_type:lower():gsub("^%l", string.upper))]),
{ entry.symbol_type:lower(), 'TelescopeResultsVariable' },
{ entry.symbol_name, 'TelescopeResultsConstant' },
tailForDisplay,
{ pathToDisplay, 'TelescopeResultsComment' },
}
end
return originalEntryTable
end
require('telescope.builtin').lsp_dynamic_workspace_symbols(options)
end
function telescopePickers.prettyBuffersPicker(localOptions)
if localOptions ~= nil and type(localOptions) ~= 'table' then
print("Options must be a table.")
return
end
options = localOptions or {}
local originalEntryMaker = telescopeMakeEntryModule.gen_from_buffer(options)
options.entry_maker = function(line)
local originalEntryTable = originalEntryMaker(line)
local displayer = telescopeEntryDisplayModule.create {
separator = " ",
items = {
{ width = fileTypeIconWidth },
{ width = nil },
{ width = nil },
{ remaining = true },
},
}
originalEntryTable.display = function(entry)
local tail, path = telescopePickers.getPathAndTail(entry.filename)
local tailForDisplay = tail .. ' '
local icon, iconHighlight = telescopeUtilities.get_devicons(tail)
return displayer {
{ icon, iconHighlight },
tailForDisplay,
{ '(' .. entry.bufnr .. ')', "TelescopeResultsNumber" },
{ path, "TelescopeResultsComment" },
}
end
return originalEntryTable
end
require('telescope.builtin').buffers(options)
end
maybe turn this into a proper extension and get easy stars :grin:
@ClintWinter would be awesome to have a git_status too
I'm using telescope a lot for lsp references and definitions and it's really hard to see what files are affected. Would be very cool if we had something similar for the lsp stuff too :)
Love all the custom pickers shared here, but the snippets were all so large, making it daunting to customize them. So I came up with a much simpler solution. Works by coloring everything after two tabs in the telescope result window, though any other unique string will also work. (I checked, and apparently no telescope picker uses two consecutive tabs, so this does not seem to have any undesired side effects.)
vim.api.nvim_create_autocmd("FileType", {
pattern = "TelescopeResults",
callback = function(ctx)
vim.api.nvim_buf_call(ctx.buf, function()
vim.fn.matchadd("TelescopeParent", "\t\t.*$")
vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" })
end)
end,
})
local function filenameFirst(_, path)
local tail = vim.fs.basename(path)
local parent = vim.fs.dirname(path)
if parent == "." then return tail end
return string.format("%s\t\t%s", tail, parent)
end
require("telescope").setup {
pickers = {
find_files = {
path_display = filenameFirst,
}
}
}
And this is the result:
This is great @chrisgrieser, I tried the other approaches in this thread but they proved too daunting :/
For any Lazyvim users that are a bit slow like me, this might save you some time: Put this code in a new [whatever].lua file under your nvim/lua dir. It's exactly like @chrisgrieser's submission above, with it turned on for git_files as well as find_files, and the last part wrapped in a return statement (without that I was getting an error: Invalid spec module: "plugins. telescope-format'. Expected a 'table' of specs, but a 'boolean' was returned instead).
vim.api.nvim_create_autocmd("FileType", {
pattern = "TelescopeResults",
callback = function(ctx)
vim.api.nvim_buf_call(ctx.buf, function()
vim.fn.matchadd("TelescopeParent", "\t\t.*$")
vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" })
end)
end,
})
local function filenameFirst(_, path)
local tail = vim.fs.basename(path)
local parent = vim.fs.dirname(path)
if parent == "." then
return tail
end
return string.format("%s\t\t%s", tail, parent)
end
return {
require("telescope").setup({
pickers = {
find_files = {
path_display = filenameFirst,
},
git_files = {
path_display = filenameFirst,
},
},
}),
}
Thanks @chrisgrieser! Works like a charm for find_files. Is there anything else needed for the rest of the pickers? I cannot seem to make git_status work:
local function filenameFirst(_, path)
local tail = vim.fs.basename(path)
local parent = vim.fs.dirname(path)
if parent == "." then return tail end
return string.format("%s\t\t%s", tail, parent)
end
vim.api.nvim_create_autocmd("FileType", {
pattern = "TelescopeResults",
callback = function(ctx)
vim.api.nvim_buf_call(ctx.buf, function()
vim.fn.matchadd("TelescopeParent", "\t\t.*$")
vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" })
end)
end,
})
require("telescope").setup({
pickers = {
git_status = { path_display = filenameFirst, },
find_files = { path_display = filenameFirst, },
},
})
Thanks @chrisgrieser! Works like a charm for
find_files. Is there anything else needed for the rest of the pickers? I cannot seem to makegit_statuswork:local function filenameFirst(_, path) local tail = vim.fs.basename(path) local parent = vim.fs.dirname(path) if parent == "." then return tail end return string.format("%s\t\t%s", tail, parent) end vim.api.nvim_create_autocmd("FileType", { pattern = "TelescopeResults", callback = function(ctx) vim.api.nvim_buf_call(ctx.buf, function() vim.fn.matchadd("TelescopeParent", "\t\t.*$") vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" }) end) end, }) require("telescope").setup({ pickers = { git_status = { path_display = filenameFirst, }, find_files = { path_display = filenameFirst, }, }, })
For anyone who wants @chrisgrieser s solution for every available picker, just add it to your defaults like this:
telescope.setup({
...
defaults = {
path_display = filenameFirst
}
...
})
just add it to your defaults like this:
Tried already. Doesn't work. Seems like this is a known bug though.
just add it to your defaults like this:
Tried already. Doesn't work. Seems like this is a known bug though.
I just fixed path_display for git_status
https://github.com/nvim-telescope/telescope.nvim/pull/2881
Love all the custom pickers shared here, but the snippets were all so large, making it daunting to customize them. So I came up with a much simpler solution. Works by coloring everything after two tabs in the telescope result window, though any other unique string will also work. (I checked, and apparently no telescope picker uses two consecutive tabs, so this does not seem to have any undesired side effects.)
vim.api.nvim_create_autocmd("FileType", { pattern = "TelescopeResults", callback = function(ctx) vim.api.nvim_buf_call(ctx.buf, function() vim.fn.matchadd("TelescopeParent", "\t\t.*$") vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" }) end) end, }) local function filenameFirst(_, path) local tail = vim.fs.basename(path) local parent = vim.fs.dirname(path) if parent == "." then return tail end return string.format("%s\t\t%s", tail, parent) end require("telescope").setup { pickers = { find_files = { path_display = filenameFirst, } } }And this is the result:
This should be one of the default options, it looks great
Love all the custom pickers shared here, but the snippets were all so large, making it daunting to customize them. So I came up with a much simpler solution. Works by coloring everything after two tabs in the telescope result window, though any other unique string will also work. (I checked, and apparently no telescope picker uses two consecutive tabs, so this does not seem to have any undesired side effects.)
vim.api.nvim_create_autocmd("FileType", { pattern = "TelescopeResults", callback = function(ctx) vim.api.nvim_buf_call(ctx.buf, function() vim.fn.matchadd("TelescopeParent", "\t\t.*$") vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" }) end) end, }) local function filenameFirst(_, path) local tail = vim.fs.basename(path) local parent = vim.fs.dirname(path) if parent == "." then return tail end return string.format("%s\t\t%s", tail, parent) end require("telescope").setup { pickers = { find_files = { path_display = filenameFirst, } } }And this is the result:
Thanks, can you please explain where this snippet should go? I'm new to both neovim and lua.
Love all the custom pickers shared here, but the snippets were all so large, making it daunting to customize them. So I came up with a much simpler solution. Works by coloring everything after two tabs in the telescope result window, though any other unique string will also work. (I checked, and apparently no telescope picker uses two consecutive tabs, so this does not seem to have any undesired side effects.)
vim.api.nvim_create_autocmd("FileType", { pattern = "TelescopeResults", callback = function(ctx) vim.api.nvim_buf_call(ctx.buf, function() vim.fn.matchadd("TelescopeParent", "\t\t.*$") vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" }) end) end, }) local function filenameFirst(_, path) local tail = vim.fs.basename(path) local parent = vim.fs.dirname(path) if parent == "." then return tail end return string.format("%s\t\t%s", tail, parent) end require("telescope").setup { pickers = { find_files = { path_display = filenameFirst, } } }And this is the result:
Thanks, can you please explain where this snippet should go? I'm new to both neovim and lua.
Hi Itamar, Feel free to check out my configuration for this:
https://github.com/silicakes/dotfiles/blob/main/.config/nvim/plugin/telescope.lua
Love all the custom pickers shared here, but the snippets were all so large, making it daunting to customize them. So I came up with a much simpler solution. Works by coloring everything after two tabs in the telescope result window, though any other unique string will also work. (I checked, and apparently no telescope picker uses two consecutive tabs, so this does not seem to have any undesired side effects.)
vim.api.nvim_create_autocmd("FileType", { pattern = "TelescopeResults", callback = function(ctx) vim.api.nvim_buf_call(ctx.buf, function() vim.fn.matchadd("TelescopeParent", "\t\t.*$") vim.api.nvim_set_hl(0, "TelescopeParent", { link = "Comment" }) end) end, }) local function filenameFirst(_, path) local tail = vim.fs.basename(path) local parent = vim.fs.dirname(path) if parent == "." then return tail end return string.format("%s\t\t%s", tail, parent) end require("telescope").setup { pickers = { find_files = { path_display = filenameFirst, } } }And this is the result:
would it be possible to do something similar for LSP workspace diagnostics ?



