feat(columns): per-character permission column highlights (#375) (#146)

Problem: the permissions column rendered as a monolithic unstyled
string, making it hard to scan `rwx` bits at a glance.

Solution: add per-character highlight groups for permission characters
following the `eza`/`lsd` convention. All groups link to standard
Neovim highlights so every colorscheme works out of the box.
This commit is contained in:
Barrett Ruth 2026-03-16 15:53:23 -04:00 committed by GitHub
parent 7b16324c5a
commit bf461f6844
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 81 additions and 3 deletions

View file

@ -868,6 +868,24 @@ OilExecutable *hl-OilExecutabl
OilExecutableHidden *hl-OilExecutableHidden* OilExecutableHidden *hl-OilExecutableHidden*
Hidden executable files in an oil buffer Hidden executable files in an oil buffer
OilPermissionRead *hl-OilPermissionRead*
Read permission character in the permissions column
OilPermissionWrite *hl-OilPermissionWrite*
Write permission character in the permissions column
OilPermissionExec *hl-OilPermissionExec*
Execute permission character in the permissions column
OilPermissionSetuid *hl-OilPermissionSetuid*
Setuid/setgid permission character in the permissions column
OilPermissionSticky *hl-OilPermissionSticky*
Sticky bit permission character in the permissions column
OilPermissionNone *hl-OilPermissionNone*
No permission character in the permissions column
OilCreate *hl-OilCreate* OilCreate *hl-OilCreate*
Create action in the oil preview window Create action in the oil preview window

View file

@ -66,7 +66,7 @@ issues against this fork.
| [#363](https://github.com/stevearc/oil.nvim/issues/363) | `prompt_save_on_select_new_entry` wrong prompt | fixed | | [#363](https://github.com/stevearc/oil.nvim/issues/363) | `prompt_save_on_select_new_entry` wrong prompt | fixed |
| [#371](https://github.com/stevearc/oil.nvim/issues/371) | Constrain cursor in insert mode | fixed ([#93](https://github.com/barrettruth/canola.nvim/pull/93)) | | [#371](https://github.com/stevearc/oil.nvim/issues/371) | Constrain cursor in insert mode | fixed ([#93](https://github.com/barrettruth/canola.nvim/pull/93)) |
| [#373](https://github.com/stevearc/oil.nvim/issues/373) | Dir from quickfix with bqf/trouble broken | open | | [#373](https://github.com/stevearc/oil.nvim/issues/373) | Dir from quickfix with bqf/trouble broken | open |
| [#375](https://github.com/stevearc/oil.nvim/issues/375) | Highlights for file types and permissions | open | | [#375](https://github.com/stevearc/oil.nvim/issues/375) | Highlights for file types and permissions | fixed — per-character permission column highlights |
| [#380](https://github.com/stevearc/oil.nvim/issues/380) | Silently overriding `show_hidden` | not actionable — counter to config intent | | [#380](https://github.com/stevearc/oil.nvim/issues/380) | Silently overriding `show_hidden` | not actionable — counter to config intent |
| [#382](https://github.com/stevearc/oil.nvim/issues/382) | Relative path in window title | not actionable — solved by `get_win_title` callback ([#482](https://github.com/stevearc/oil.nvim/pull/482)) | | [#382](https://github.com/stevearc/oil.nvim/issues/382) | Relative path in window title | not actionable — solved by `get_win_title` callback ([#482](https://github.com/stevearc/oil.nvim/pull/482)) |
| [#392](https://github.com/stevearc/oil.nvim/issues/392) | Option to skip delete prompt | fixed | | [#392](https://github.com/stevearc/oil.nvim/issues/392) | Option to skip delete prompt | fixed |

View file

@ -100,7 +100,8 @@ if not fs.is_windows then
if not stat then if not stat then
return columns.EMPTY return columns.EMPTY
end end
return permissions.mode_to_str(stat.mode) local str = permissions.mode_to_str(stat.mode)
return { str, permissions.mode_to_highlights(str) }
end, end,
parse = function(line, conf) parse = function(line, conf)

View file

@ -25,6 +25,31 @@ M.mode_to_str = function(mode)
.. perm_to_str(bit.band(extra, 1) ~= 0 and 't', mode) .. perm_to_str(bit.band(extra, 1) ~= 0 and 't', mode)
end end
local char_hl = {
r = 'OilPermissionRead',
w = 'OilPermissionWrite',
x = 'OilPermissionExec',
s = 'OilPermissionSetuid',
S = 'OilPermissionSetuid',
t = 'OilPermissionSticky',
T = 'OilPermissionSticky',
['-'] = 'OilPermissionNone',
}
---@param str string
---@return table[]
M.mode_to_highlights = function(str)
local highlights = {}
for i = 1, #str do
local c = str:sub(i, i)
local hl = char_hl[c]
if hl then
table.insert(highlights, { hl, i - 1, i })
end
end
return highlights
end
---@param mode integer ---@param mode integer
---@return string ---@return string
M.mode_to_octal_str = function(mode) M.mode_to_octal_str = function(mode)

View file

@ -118,7 +118,11 @@ local ssh_columns = {}
ssh_columns.permissions = { ssh_columns.permissions = {
render = function(entry, conf) render = function(entry, conf)
local meta = entry[FIELD_META] local meta = entry[FIELD_META]
return meta and permissions.mode_to_str(meta.mode) if not meta then
return
end
local str = permissions.mode_to_str(meta.mode)
return { str, permissions.mode_to_highlights(str) }
end, end,
parse = function(line, conf) parse = function(line, conf)

View file

@ -1066,6 +1066,36 @@ M._get_highlights = function()
link = 'OilHidden', link = 'OilHidden',
desc = 'Hidden executable files in an oil buffer', desc = 'Hidden executable files in an oil buffer',
}, },
{
name = 'OilPermissionRead',
link = 'DiagnosticWarn',
desc = 'Read permission character in the permissions column',
},
{
name = 'OilPermissionWrite',
link = 'DiagnosticError',
desc = 'Write permission character in the permissions column',
},
{
name = 'OilPermissionExec',
link = 'DiagnosticOk',
desc = 'Execute permission character in the permissions column',
},
{
name = 'OilPermissionSetuid',
link = 'DiagnosticError',
desc = 'Setuid/setgid permission character in the permissions column',
},
{
name = 'OilPermissionSticky',
link = 'DiagnosticInfo',
desc = 'Sticky bit permission character in the permissions column',
},
{
name = 'OilPermissionNone',
link = 'NonText',
desc = 'No permission character in the permissions column',
},
{ {
name = 'OilCreate', name = 'OilCreate',
link = 'DiagnosticInfo', link = 'DiagnosticInfo',