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

adding file icons #244

Merged
merged 5 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Grug find! Grug replace! Grug happy!
- [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) >= 14 recommended
- a [Nerd Font](https://www.nerdfonts.com/) **_(optional)_**
- [ast-grep](https://ast-grep.github.io) **_(optional)_** if you would like to use the `ast-grep` search engine. Version >= `0.25.7` if you would like context lines flags to work.
- either [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) or [mini.icons](https://github.com/echasnovski/mini.icons) for file icons support **_(optional)_**

Run `:checkhealth grug-far` if you see unexpected issues.

Expand Down
5 changes: 5 additions & 0 deletions lua/grug-far.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local close = require('grug-far/actions/close')
local engine = require('grug-far/engine')
local fold = require('grug-far/fold')
local inputs = require('grug-far/inputs')
local fileIconsProvider = require('grug-far/fileIconsProvider')

local M = {}

Expand Down Expand Up @@ -143,6 +144,7 @@ local contextCount = 0
---@field prevWin? integer
---@field actions GrugFarAction[]
---@field engine GrugFarEngine
---@field fileIconsProvider? FileIconsProvider

--- generate instance specific context
---@param options GrugFarOptions
Expand All @@ -160,6 +162,9 @@ local function createContext(options)
augroup = vim.api.nvim_create_augroup('grug-far.nvim-augroup-' .. contextCount, {}),
extmarkIds = {},
actions = {},
fileIconsProvider = options.icons.enabled and fileIconsProvider.getProvider(
options.icons.fileIconsProvider
) or nil,
state = {
inputs = {},
headerRow = 0,
Expand Down
3 changes: 2 additions & 1 deletion lua/grug-far/engine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ local M = {}

---@class ResultHighlightSign
---@field hl string
---@field icon string
---@field icon? string
---@field text? string

---@enum ResultHighlightType
M.ResultHighlightType = {
Expand Down
88 changes: 88 additions & 0 deletions lua/grug-far/fileIconsProvider.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
local M = {}

---@class FileIconsProvider
---@field type FileIconsProviderType
---@field _lib? any
---@field get_lib fun():(lib: any)
---@field get_icon fun(lib: any, path: string):(icon:string, icon_hl: string)

---@type FileIconsProvider[]
local providers = {
{
type = 'nvim-web-devicons',
get_lib = function()
local found, lib = pcall(require, 'nvim-web-devicons')
if not found then
return nil
end

-- check if setup() called
if not lib.has_loaded() then
return nil
end

return lib
end,
get_icon = function(self, path)
-- first, try to match extensions like .spec.js
local basename = vim.fs.basename(path)
local multi_dot_extension = basename:match('[^.]*%.(.+)$')
local icon, hl = self._lib.get_icon(path, multi_dot_extension, { default = false })
if icon then
return icon, hl
end

local extension = string.match(path, '.+%.(.+)$')
return self._lib.get_icon(path, extension, { default = true })
end,
},
{
type = 'mini.icons',
get_lib = function()
local found, lib = pcall(require, 'mini.icons')
if not found then
return nil
end
-- according to mini.icons docs, need to check this
-- to make sure setup has been called!
-- selene: allow(global_usage)
---@diagnostic disable-next-line
if not _G.MiniIcons then
return nil
end

return lib
Comment on lines +42 to +54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MagicDuck, hi! Saw your Reddit post about adding support for 'mini.icons' and decided to see if it does the _G.MiniIcons check. It does, thanks for reading 'mini.icons' help page :)

One thing I'd like to mention is that this whole get_lib can be compressed to a single return _G.MiniIcons. This object is both an indication that user has run require('mini.icons').setup() (i.e. opted in for using it and not only has it available from the full 'mini.nvim' install) and the whole require('mini.icons') table itself.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, no problem! Thank you for building mini.icons! :)
I’ll make a note for myself to update this if I get a chance.

end,
get_icon = function(self, path)
return self._lib.get('file', path)
end,
},
}

--- gets the icons provider
---@param type FileIconsProviderType
function M.getProvider(type)
if type == false then
return nil
end

for _, provider in ipairs(providers) do
local lib = provider.get_lib()

if lib then
if type == 'first_available' or provider.type == type then
local new_provider = vim.deepcopy(provider)
new_provider._lib = lib
return new_provider
end
else
if provider.type == type then
return nil
end
end
end

return nil
end

return M
8 changes: 8 additions & 0 deletions lua/grug-far/opts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ M.defaultOptions = {
-- whether to show icons
enabled = true,

-- provider to use for file icons
-- acceptable values: 'first_available', 'nvim-web-devicons', 'mini.icons', false (to disable)
fileIconsProvider = 'first_available',

actionEntryBullet = ' ',

searchInput = ' ',
Expand Down Expand Up @@ -352,8 +356,11 @@ M.defaultOptions = {
---@field historyDir? string
---@field autoSave? AutoSaveTable

---@alias FileIconsProviderType "first_available" | "mini.icons" | "nvim-web-devicons" | false

---@class IconsTable
---@field enabled boolean
---@field fileIconsProvider FileIconsProviderType
---@field searchInput string
---@field actionEntryBullet string
---@field replaceInput string
Expand All @@ -370,6 +377,7 @@ M.defaultOptions = {

---@class IconsTableOverride
---@field enabled? boolean
---@field fileIconsProvider? FileIconsProviderType
---@field searchInput? string
---@field actionEntryBullet? string
---@field replaceInput? string
Expand Down
20 changes: 16 additions & 4 deletions lua/grug-far/render/resultsList.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ end
---@class addLocationMarkOpts
---@field sign? ResultHighlightSign
---@field matchLineCount? integer
---@field virt_text? string[][]
---@field virt_text_pos? string

--- adds location mark
---@param buf integer
Expand All @@ -30,7 +32,10 @@ end
---@param options addLocationMarkOpts
---@return integer markId
local function addLocationMark(buf, context, line, end_col, options)
local sign_text = options.sign and opts.getIcon(options.sign.icon, context) or nil
local sign_text = nil
if options.sign then
sign_text = options.sign.text or opts.getIcon(options.sign.icon, context)
end
local resultLocationOpts = context.options.resultLocation

return vim.api.nvim_buf_set_extmark(buf, context.locationsNamespace, line, 0, {
Expand All @@ -45,11 +50,11 @@ local function addLocationMark(buf, context, line, end_col, options)
resultLocationOpts.numberLabelFormat:format(options.matchLineCount),
'GrugFarResultsNumberLabel',
},
} or nil,
} or options.virt_text,
virt_text_pos = resultLocationOpts.showNumberLabel
and options.matchLineCount
and resultLocationOpts.numberLabelPosition
or nil,
or options.virt_text_pos,
})
end

Expand Down Expand Up @@ -117,7 +122,14 @@ function M.appendResultsChunk(buf, context, data)

if hl_type == ResultHighlightType.FilePath then
state.resultsLastFilename = string.sub(line, highlight.start_col + 1, highlight.end_col + 1)
local markId = addLocationMark(buf, context, lastline + highlight.start_line, #line, {})
local options = {}
if context.fileIconsProvider then
local icon, icon_hl = context.fileIconsProvider:get_icon(state.resultsLastFilename)
options.virt_text = { { icon .. ' ', icon_hl } }
options.virt_text_pos = 'inline'
end

local markId = addLocationMark(buf, context, lastline + highlight.start_line, #line, options)
resultLocationByExtmarkId[markId] = { filename = state.resultsLastFilename }
elseif hl_type == ResultHighlightType.LineNumber then
-- omit ending ':'
Expand Down
Loading