feat(icon): add opt-in filetype detection via file contents
Problem: files without standard extensions (e.g. scripts with shebangs, Makefile, Dockerfile) got incorrect or default icons since icon providers match by filename or extension only. Solution: add use_slow_filetype_detection option to the icon column config. When enabled, reads the first 16 lines of each file and passes them to vim.filetype.match for content-based detection. The detected filetype is forwarded to mini.icons or nvim-web-devicons as a trailing parameter, preserving backwards compatibility with existing icon provider implementations. Based on: stevearc/oil.nvim#618
This commit is contained in:
parent
41556ec87f
commit
ded17258cd
4 changed files with 36 additions and 5 deletions
|
|
@ -438,6 +438,10 @@ icon *column-ico
|
||||||
{directory} `string` Icon for directories
|
{directory} `string` Icon for directories
|
||||||
{add_padding} `boolean` Set to false to remove the extra whitespace after
|
{add_padding} `boolean` Set to false to remove the extra whitespace after
|
||||||
the icon
|
the icon
|
||||||
|
{use_slow_filetype_detection} `boolean` Set to true to detect filetypes
|
||||||
|
by reading the first lines of each file (e.g. shebangs).
|
||||||
|
This improves icon accuracy for extensionless scripts but
|
||||||
|
performs synchronous I/O per file per render.
|
||||||
|
|
||||||
size *column-size*
|
size *column-size*
|
||||||
Adapters: files, ssh, s3
|
Adapters: files, ssh, s3
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ end
|
||||||
local icon_provider = util.get_icon_provider()
|
local icon_provider = util.get_icon_provider()
|
||||||
if icon_provider then
|
if icon_provider then
|
||||||
M.register("icon", {
|
M.register("icon", {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf, bufnr)
|
||||||
local field_type = entry[FIELD_TYPE]
|
local field_type = entry[FIELD_TYPE]
|
||||||
local name = entry[FIELD_NAME]
|
local name = entry[FIELD_NAME]
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
|
|
@ -168,7 +168,20 @@ if icon_provider then
|
||||||
if meta and meta.display_name then
|
if meta and meta.display_name then
|
||||||
name = meta.display_name
|
name = meta.display_name
|
||||||
end
|
end
|
||||||
local icon, hl = icon_provider(field_type, name, conf)
|
|
||||||
|
local ft = nil
|
||||||
|
if conf and conf.use_slow_filetype_detection and field_type == "file" then
|
||||||
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
|
local _, path = util.parse_url(bufname)
|
||||||
|
if path then
|
||||||
|
local lines = vim.fn.readfile(path .. name, "", 16)
|
||||||
|
if lines and #lines > 0 then
|
||||||
|
ft = vim.filetype.match({ filename = name, contents = lines })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local icon, hl = icon_provider(field_type, name, conf, ft)
|
||||||
if not conf or conf.add_padding ~= false then
|
if not conf or conf.add_padding ~= false then
|
||||||
icon = icon .. " "
|
icon = icon .. " "
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ local FIELD_NAME = constants.FIELD_NAME
|
||||||
local FIELD_TYPE = constants.FIELD_TYPE
|
local FIELD_TYPE = constants.FIELD_TYPE
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
||||||
---@alias oil.IconProvider fun(type: string, name: string, conf: table?): (icon: string, hl: string)
|
---@alias oil.IconProvider fun(type: string, name: string, conf: table?, ft: string?): (icon: string, hl: string)
|
||||||
|
|
||||||
---@param url string
|
---@param url string
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
|
|
@ -934,7 +934,10 @@ M.get_icon_provider = function()
|
||||||
local _, mini_icons = pcall(require, "mini.icons")
|
local _, mini_icons = pcall(require, "mini.icons")
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup
|
if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup
|
||||||
return function(type, name)
|
return function(type, name, conf, ft)
|
||||||
|
if ft then
|
||||||
|
return mini_icons.get("filetype", ft)
|
||||||
|
end
|
||||||
return mini_icons.get(type == "directory" and "directory" or "file", name)
|
return mini_icons.get(type == "directory" and "directory" or "file", name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -942,10 +945,16 @@ M.get_icon_provider = function()
|
||||||
-- fallback to `nvim-web-devicons`
|
-- fallback to `nvim-web-devicons`
|
||||||
local has_devicons, devicons = pcall(require, "nvim-web-devicons")
|
local has_devicons, devicons = pcall(require, "nvim-web-devicons")
|
||||||
if has_devicons then
|
if has_devicons then
|
||||||
return function(type, name, conf)
|
return function(type, name, conf, ft)
|
||||||
if type == "directory" then
|
if type == "directory" then
|
||||||
return conf and conf.directory or "", "OilDirIcon"
|
return conf and conf.directory or "", "OilDirIcon"
|
||||||
else
|
else
|
||||||
|
if ft then
|
||||||
|
local ft_icon, ft_hl = devicons.get_icon_by_filetype(ft)
|
||||||
|
if ft_icon and ft_icon ~= "" then
|
||||||
|
return ft_icon, ft_hl
|
||||||
|
end
|
||||||
|
end
|
||||||
local icon, hl = devicons.get_icon(name)
|
local icon, hl = devicons.get_icon(name)
|
||||||
icon = icon or (conf and conf.default_file or "")
|
icon = icon or (conf and conf.default_file or "")
|
||||||
return icon, hl
|
return icon, hl
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,11 @@ COL_DEFS = [
|
||||||
"boolean",
|
"boolean",
|
||||||
"Set to false to remove the extra whitespace after the icon",
|
"Set to false to remove the extra whitespace after the icon",
|
||||||
),
|
),
|
||||||
|
LuaParam(
|
||||||
|
"use_slow_filetype_detection",
|
||||||
|
"boolean",
|
||||||
|
"Set to true to detect filetypes by reading the first lines of each file (e.g. shebangs).",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ColumnDef("size", "files, ssh, s3", False, True, "The size of the file", UNIVERSAL + []),
|
ColumnDef("size", "files, ssh, s3", False, True, "The size of the file", UNIVERSAL + []),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue