Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to make file name more visible in file path #2014

Closed
GuillaumeLagrange opened this issue Jun 16, 2022 · 46 comments · Fixed by #3010
Closed

Ability to make file name more visible in file path #2014

GuillaumeLagrange opened this issue Jun 16, 2022 · 46 comments · Fixed by #3010
Labels
enhancement Enhancement to performance, inner workings or existent features

Comments

@GuillaumeLagrange
Copy link

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 !

@GuillaumeLagrange GuillaumeLagrange added the enhancement Enhancement to performance, inner workings or existent features label Jun 16, 2022
@Conni2461
Copy link
Member

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,

@vignesh0025
Copy link

I too require this feature implemented. Thanks

@DasOhmoff
Copy link

Yes please. I would love this too!

@XobSod
Copy link

XobSod commented Feb 1, 2023

Bumping +1 ;)

@JoosepAlviste
Copy link

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:

image

But in Telescope, the file name (first section before the dash) is not very easy to discern from the full file path:

image

@tjex
Copy link

tjex commented May 9, 2023

agreed, this would be ace!

@towry
Copy link

towry commented May 10, 2023

截屏2023-05-10 08 38 55

@tjex
Copy link

tjex commented May 10, 2023

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.

@towry
Copy link

towry commented May 10, 2023

@tjex
Here it is 😁

pickers.lua

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

@uloco
Copy link

uloco commented May 16, 2023

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!

@SandeepTuniki
Copy link

@towry Any chance you can post the code snippet here? I'm getting a 404 on clicking the link (repo is taken private?).

@tjex
Copy link

tjex commented Jul 9, 2023

@SandeepTuniki the snippet is there.
#2014 (comment)

@SandeepTuniki
Copy link

@SandeepTuniki the snippet is there. #2014 (comment)

Ah yes. Looks like the comment was updated from a link to the code snippet (thanks, @towry!)

@gilitos92
Copy link

gilitos92 commented Aug 23, 2023

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.

CleanShot 23-08-2023at 13 38 12@2x
CleanShot 23-08-2023at 13 38 44@2x

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

@otavioschwanck
Copy link

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.

CleanShot 23-08-2023at 13 38 12@2x CleanShot 23-08-2023at 13 38 44@2x

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

@otavioschwanck
Copy link

@towry do you have a buffers picker too?

@ClintWinter
Copy link

Here's an addition to @towry's for lsp_document_symbols, lsp_dynamic_workspace_symbols, and buffers.

Buffers:
Screenshot 2023-10-06 at 9 27 58 AM

Document symbols:
Screenshot 2023-10-06 at 9 28 07 AM

Workspace symbols:
Screenshot 2023-10-06 at 9 28 19 AM

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

@jamestrew
Copy link
Contributor

maybe turn this into a proper extension and get easy stars 😁

@otavioschwanck
Copy link

@ClintWinter would be awesome to have a git_status too

aaronmcadam added a commit to aaronmcadam/dotfiles that referenced this issue Oct 17, 2023
@uloco
Copy link

uloco commented Nov 3, 2023

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 :)

@chrisgrieser
Copy link
Contributor

chrisgrieser commented Jan 1, 2024

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:
Showcase telescope paths

@mturoci
Copy link

mturoci commented Jan 10, 2024

just add it to your defaults like this:

Tried already. Doesn't work. Seems like this is a known bug though.

@jamestrew
Copy link
Contributor

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
#2881

@Skydler
Copy link

Skydler commented Jan 30, 2024

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: Showcase telescope paths

This should be one of the default options, it looks great

@itamark-targa
Copy link

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: Showcase telescope paths

Thanks, can you please explain where this snippet should go? I'm new to both neovim and lua.

@silicakes
Copy link

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: Showcase telescope paths

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

eriklindebratt added a commit to eriklindebratt/dotfiles that referenced this issue Feb 13, 2024
Files in results list now show filenames first, followed by file path,
slightly but dimmed. This thanks to nvim-telescope/telescope.nvim#2014 (comment).
p2kmgcl added a commit to p2kmgcl/chachi-shell that referenced this issue Feb 21, 2024
@kronolynx
Copy link

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: Showcase telescope paths

would it be possible to do something similar for LSP workspace diagnostics ?

@marcelarie
Copy link
Contributor

marcelarie commented Mar 14, 2024

Hi everyone, I am searching for a simpler customization, with lsp_references I get this word preview that cuts the file path:

image

Do you know if there is a way to disable that? I will prefer to see the full file path and then check on the preview what word was selected. It might be that I need to change the ripgrep command parameters?

@Twotyth
Copy link

Twotyth commented Mar 14, 2024

would it be possible to do something similar for LSP workspace diagnostics ?

@kronolynx I actually got this entry maker going for a couple of days. It also shows the symbol icon and its qualifier then shows it instead of filepath if possible. Take a look:

local M = {}


local entry_display = require('telescope.pickers.entry_display')

local symbol_to_icon_map = {
    ['class'] = { icon = '', hi = 'TelescopeResultClass' },
    ['type'] = { icon = '', hi = 'TelescopeResultClass' },
    ['struct'] = { icon = '', hi = 'TelescopeResultStruct' },
    ['enum'] = { icon = '', hi = 'TelescopeResultClass' },
    ['union'] = { icon = '', hi = 'TelescopeResultClass' },
    ['interface'] = { icon = '', hi = 'TelescopeResultMethod' },
    ['method'] = { icon = '', hi = 'TelescopeResultMethod' },
    ['function'] = { icon = 'ƒ ', hi = 'TelescopeResultFunction' },
    ['constant'] = { icon = '', hi = 'TelescopeResultConstant' },
    ['field'] = { icon = '', hi = 'TelescopeResultField' },
    ['property'] = { icon = '', hi = 'TelescopeResultField' }
}

local displayer = entry_display.create({
    separator = ' ',
    items = {
        { width = 2 },
        { remaining = true }
    }
})


function M.maker()
    local entry_maker = require('telescope.make_entry').gen_from_lsp_symbols({})
    return function(line)
        local originalEntryTable = entry_maker(line)
        originalEntryTable.display = function (entry)
            local kind_and_higr = symbol_to_icon_map[entry.symbol_type:lower()] or { icon = '', hi = 'TelescopeResultsNormal' }
            local dot_idx = entry.symbol_name:reverse():find("%.") or entry.symbol_name:reverse():find("::")
            local symbol, qualifiier

            if dot_idx == nil then
                symbol = entry.symbol_name
                qualifiier = entry.filename
            else
                symbol = entry.symbol_name:sub(1 - dot_idx)
                qualifiier = entry.symbol_name:sub(1, #entry.symbol_name - #symbol - 1)
            end

            return displayer({
                { kind_and_higr.icon, kind_and_higr.hi },
                string.format("%s\t\tin %s", symbol, qualifiier)
            })
        end

        return originalEntryTable
    end
end


return M;

And its used like this:

require('telescope').setup({
    pickers = {
        lsp_dynamic_workspace_symbols = {
            entry_maker = require('twoty.custom.telescope-symbol-entry-maker').maker()
        }
    },
})

Feel free to use this. And if anyone thinks of a way to make this better, please share and post it smiley face

@simeonoff
Copy link

Here's one I made for grapple.nvim:

function telescopePickers.prettyGrapplePicker(localOptions)
	local Grapple = require("grapple")

	if localOptions ~= nil and type(localOptions) ~= "table" then
		print("Options must be a table.")
		return
	end

	local options = localOptions or {}

	local generate_finder = function()
		local tags, err = Grapple.tags()

		if not tags then
			---@diagnostic disable-next-line: param-type-mismatch
			return vim.notify(err, vim.log.levels.ERROR)
		end

		local results = {}
		for i, tag in ipairs(tags) do
			---@class grapple.telescope.result
			local result = {
				i,
				tag.path,
				tag.cursor and tag.cursor[1],
				tag.cursor and tag.cursor[2],
			}

			table.insert(results, result)
		end

		local displayer = telescopeEntryDisplayModule.create({
			separator = " ",
			items = {
				{ width = fileTypeIconWidth },
				{ width = nil },
				{ remaining = true },
			},
		})

		return finders.new_table({
			results = results,

			---@param result grapple.telescope.result
			entry_maker = function(result)
				local filename = result[2]
				local lnum = result[3]

				local entry = {
					value = result,
					ordinal = filename,
					filename = filename,
					lnum = lnum,
					display = function(entry)
						local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.value[2])
						local icon, iconHighlight = telescopeUtilities.get_devicons(tail)

						return displayer({
							{ icon, iconHighlight },
							{ tail },
							{ pathToDisplay, "TelescopeResultsComment" },
						})
					end,
				}

				return entry
			end,
		})
	end

	local function delete_tag(prompt_bufnr)
		local selection = action_state.get_selected_entry()

		Grapple.untag({ path = selection.filename })

		local current_picker = action_state.get_current_picker(prompt_bufnr)
		current_picker:refresh(generate_finder(), { reset_prompt = true })
	end

	pickers
		.new(options, {
			prompt_title = "Grapple",
			finder = generate_finder(),
			sorter = conf.generic_sorter({}),
			previewer = conf.file_previewer({}),
			results_title = "Grapple Tags",
			attach_mappings = function(_, map)
				map("i", "<C-X>", delete_tag)
				map("n", "<C-X>", delete_tag)
				return true
			end,
		})
		:find()
end
Screenshot 2024-03-18 at 07 05 51

@kronolynx
Copy link

@kronolynx I actually got this entry maker going for a couple of days. It also shows the symbol icon and its qualifier then shows it instead of filepath if possible. Take a look:
....
Feel free to use this. And if anyone thinks of a way to make this better, please share and post it smiley face

Thank you @Twotyth but I was looking for workspace diagnostics

@alycklama
Copy link
Contributor

I think I found a nice way to do highlighting in a flexible way. I'm lacking a bit of time right now, so I made a very simple proof-of-concept:

make_entry.lua

    mt_file_entry.display = function(entry)
      local hl_group, icon
      local display = utils.transform_path(opts, entry.value)
     -- local display, path_style = utils.transform_path(opts, entry.value)

      display, hl_group, icon = utils.transform_devicons(entry.value, display, disable_devicons)
       
     -- path_style would be returned from utils.transform_path (see commented code above)
      local path_style = { { 10, 20, }, hl_group }
      local style = { { { 0, #icon, }, hl_group }, path_style }

      if hl_group then
        return display, style
      else
        return display
      end
    end

When utils.transform_path returns both the transformed_path, as well as the path_style, you can easily merge the styles, resulting in the image below. The 10th up until the 20th character take the color of the icon. This is now statically done in the example above, but imagine every transform_path to decide for itself what styling to return. It would make it quite powerful and flexible.

image

What do you think? I'll work on a real example this week so you can see how it would look like.

@marcelarie
Copy link
Contributor

Anyone here knows how to fix this?

@simeonoff
Copy link

simeonoff commented Mar 26, 2024

@marcelarie Here's what mine looks like and the code for it:

Screenshot 2024-03-26 at 11 01 32
function telescopePickers.prettyLspReferences(localOptions)
	if localOptions ~= nil and type(localOptions) ~= "table" then
		print("Options must be a table.")
		return
	end

	local options = localOptions or {}

	local originalEntryMaker = telescopeMakeEntryModule.gen_from_quickfix(options)

	options.entry_maker = function(line)
		local originalEntryTable = originalEntryMaker(line)

		local displayer = telescopeEntryDisplayModule.create({
			separator = " ", -- Telescope will use this separator between each entry item
			items = {
				{ width = fileTypeIconWidth },
				{ width = nil },
				{ remaining = true },
			},
		})

		originalEntryTable.display = function(entry)
			local tail, pathToDisplay = telescopePickers.getPathAndTail(entry.filename)
			local tailForDisplay = tail .. " "
			local icon, iconHighlight = telescopeUtilities.get_devicons(tail)
			local coordinates = string.format("  %s:%s ", entry.lnum, entry.col)

			return displayer({
				{ icon, iconHighlight },
				tailForDisplay .. coordinates,
				{ pathToDisplay, "TelescopeResultsComment" },
			})
		end

		return originalEntryTable
	end

	require("telescope.builtin").lsp_references(options)
end

@marcelarie
Copy link
Contributor

Thanks @simeonoff, where should this function go in the telescope setup?

@jamestrew jamestrew linked a pull request Mar 28, 2024 that will close this issue
@jamestrew
Copy link
Contributor

@alycklama has put together a PR (#3010) for this.
I want to get some opinions on the option name for this.
Currently it's path_display = { 'reverse' }. (the 'reverse' part is the part in question)

Does this sound good to everyone? Emote with 🚀 if you think it's reasonable or you can offer an alternative.

@jan-xyz
Copy link
Contributor

jan-xyz commented Mar 28, 2024

I would rather suggest filename_first as it is not really reversing it technically speaking.

@alycklama
Copy link
Contributor

alycklama commented Mar 28, 2024

I would rather suggest filename_first as it is not really reversing it technically speaking.

I agree, it's something I struggled with as the directory structure is actually reserved. Should it be called inversed perhaps?

@jan-xyz
Copy link
Contributor

jan-xyz commented Mar 29, 2024

what speaks against filename_first? I think it conveys quite nicely that only the filename is affected and everything else stays the same. inversed has the same problem as reversed, IMO, in that it sounds like the entire path is affected.

@alycklama
Copy link
Contributor

what speaks against filename_first? I think it conveys quite nicely that only the filename is affected and everything else stays the same. inversed has the same problem as reversed, IMO, in that it sounds like the entire path is affected.

I have nothing against the name, it's just not what my PR has implemented.

Given the input /etc/config/my_file.

What filename first would do

myfile /etc/config

What reverse / inverse does

myfile config/etc/

The reason I want to inverse the directory structure is because I work with deeply nested code, and I'm not interested in the top directories at all.

What about calling it filename_first with a sub-option reverse_dirs = true?

@jan-xyz
Copy link
Contributor

jan-xyz commented Mar 29, 2024

Oh that wasn't clear to me, as you mentioned in the PR text that you aimed for something similar to Intelij's UI, and I believe there it is not inverting the entire path, just putting the file name first, followed by the directory/package it is in.

I think that's a bit more intuitive than inverting the entire path, what do you think?

image

@jamestrew
Copy link
Contributor

What about calling it filename_first with a sub-option reverse_dirs = true?

This sounds good to me.

@towry
Copy link

towry commented Apr 11, 2024

For anyone who also use fzf-lua:

https://www.reddit.com/r/neovim/comments/1c1id24/vscode_like_path_display_in_fzfluas_files_picker/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to performance, inner workings or existent features
Projects
None yet
Development

Successfully merging a pull request may close this issue.