From 51fdab3060365d823db13cd30d8a85642dd13716 Mon Sep 17 00:00:00 2001 From: brianhuster Date: Thu, 6 Feb 2025 22:03:42 +0700 Subject: [PATCH 1/3] feat(icons): use virtual text if possible (has('nvim') or has('textprop')) Closes #145, #253 --- autoload/dirvish.vim | 29 ++++++++++++++++++++++++----- doc/dirvish.txt | 6 ++++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/autoload/dirvish.vim b/autoload/dirvish.vim index fe310ea..10d0149 100644 --- a/autoload/dirvish.vim +++ b/autoload/dirvish.vim @@ -426,7 +426,20 @@ func! s:apply_icons() abort if 0 == len(s:cb_map) return endif - highlight clear Conceal + let l:feat = has('nvim-0.8') ? 'extmark' : ((v:version >= 901 && has('textprop'))? 'textprop': 'conceal') + if l:feat ==# 'extmark' + if !exists('s:ns_id') + let s:ns_id = nvim_create_namespace('dirvish.icons') + endif + elseif l:feat ==# 'textprop' + if !exists('s:prop_type') + let s:prop_type = 'dirvish.icons' + call prop_type_add(s:prop_type, {}) + endif + else + highlight clear Conceal + endif + let i = 0 for f in getline(1, '$') let i += 1 @@ -438,10 +451,16 @@ func! s:apply_icons() abort endif endfor if icon != '' - let isdir = (f[-1:] == s:sep) - let f = substitute(s:f(f), escape(s:sep,'\').'$', '', 'g') " Full path, trim slash. - let tail_esc = escape(fnamemodify(f,':t').(isdir?(s:sep):''), '[,*.^$~\') - exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.icon + if l:feat ==# 'extmark' + call nvim_buf_set_extmark(0, s:ns_id, i-1, 0, #{virt_text: [[icon, 'DirvishColumnHead']], virt_text_pos: 'inline'}) + elseif l:feat ==# 'textprop' + call prop_add(i, 1, #{type: s:prop_type, text: icon}) + else + let isdir = (f[-1:] == s:sep) + let f = substitute(s:f(f), escape(s:sep,'\').'$', '', 'g') " Full path, trim slash. + let tail_esc = escape(fnamemodify(f,':t').(isdir?(s:sep):''), '[,*.^$~\') + exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.icon + endif endif endfor endf diff --git a/doc/dirvish.txt b/doc/dirvish.txt index 6253f70..56fed1b 100644 --- a/doc/dirvish.txt +++ b/doc/dirvish.txt @@ -114,10 +114,12 @@ dirvish#add_icon_fn(fn) a given path, wins. Best practice: if you don't have anything meaningful to show for a given path, return empty string (or whitespace). - {fn} is any |Funcref| that takes a path (string) and returns a single - character (the "icon"). Example: > + {fn} is any |Funcref| that takes a path (string) and returns a string + (the "icon"). Example: >vim call dirvish#add_icon_fn({p -> p[-1:]=='/'?'📂':'📄'}) < + Note: multi-character icons are only supported on Nvim 0.8+ or Vim 9.1+ + with |+textprop|. *dirvish#remove_icon_fn()* dirvish#remove_icon_fn(fn_id) From 0e820f1beb0b70d1926122d437a2eddb8eac4788 Mon Sep 17 00:00:00 2001 From: brianhuster Date: Fri, 7 Feb 2025 01:35:52 +0700 Subject: [PATCH 2/3] WIP: add support for highlight group for icons in Neovim --- .gitignore | 1 + autoload/dirvish.vim | 33 ++++++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08f2446 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.nvim.lua diff --git a/autoload/dirvish.vim b/autoload/dirvish.vim index 10d0149..3aa1970 100644 --- a/autoload/dirvish.vim +++ b/autoload/dirvish.vim @@ -426,12 +426,14 @@ func! s:apply_icons() abort if 0 == len(s:cb_map) return endif - let l:feat = has('nvim-0.8') ? 'extmark' : ((v:version >= 901 && has('textprop'))? 'textprop': 'conceal') - if l:feat ==# 'extmark' + if !exists('s:virttext_feat') + let s:virttext_feat = has('nvim-0.8') ? 'extmark' : ((v:version >= 901 && has('textprop'))? 'textprop': 'conceal') + endif + if s:virttext_feat ==# 'extmark' if !exists('s:ns_id') let s:ns_id = nvim_create_namespace('dirvish.icons') endif - elseif l:feat ==# 'textprop' + elseif s:virttext_feat ==# 'textprop' if !exists('s:prop_type') let s:prop_type = 'dirvish.icons' call prop_type_add(s:prop_type, {}) @@ -443,25 +445,30 @@ func! s:apply_icons() abort let i = 0 for f in getline(1, '$') let i += 1 - let icon = '' for id in sort(keys(s:cb_map)) - let icon = s:cb_map[id](f) - if -1 != match(icon, '\S') - break + let l:icon = s:cb_map[id](f) + if type(l:icon) == v:t_string + if -1 != match(l:icon, '\S') + break + endif endif endfor - if icon != '' - if l:feat ==# 'extmark' - call nvim_buf_set_extmark(0, s:ns_id, i-1, 0, #{virt_text: [[icon, 'DirvishColumnHead']], virt_text_pos: 'inline'}) - elseif l:feat ==# 'textprop' - call prop_add(i, 1, #{type: s:prop_type, text: icon}) + if exists('l:icon') + if s:virttext_feat ==# 'extmark' + let l:virt_text = type(l:icon) == v:t_dict ? [[l:icon.icon, l:icon.hl]] : [[l:icon, 'DirvishColumnHead']] + call nvim_buf_set_extmark(0, s:ns_id, i-1, 0, #{virt_text: l:virt_text, virt_text_pos: 'inline'}) + elseif s:virttext_feat ==# 'textprop' + if type(l:icon) == v:t_string + call prop_add(i, 1, #{type: s:prop_type, text: l:icon}) + endif else let isdir = (f[-1:] == s:sep) let f = substitute(s:f(f), escape(s:sep,'\').'$', '', 'g') " Full path, trim slash. let tail_esc = escape(fnamemodify(f,':t').(isdir?(s:sep):''), '[,*.^$~\') - exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.icon + exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.l:icon endif endif + unlet l:icon endfor endf From 8174cf133862c1ce2c19f8d82a1b2ff9d4fead01 Mon Sep 17 00:00:00 2001 From: brianhuster Date: Fri, 7 Feb 2025 11:23:52 +0700 Subject: [PATCH 3/3] feat(icons): Add support for highlight groups in icons in Vim --- .editorconfig | 4 ++++ .gitignore | 1 - autoload/dirvish.vim | 27 ++++++++++++++++----------- doc/dirvish.txt | 23 +++++++++++++++++++---- 4 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 .editorconfig delete mode 100644 .gitignore diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..50db69d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*] +indent_style = space +indent_size = 2 +tab_width = 2 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 08f2446..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.nvim.lua diff --git a/autoload/dirvish.vim b/autoload/dirvish.vim index 3aa1970..2cd86bf 100644 --- a/autoload/dirvish.vim +++ b/autoload/dirvish.vim @@ -4,6 +4,7 @@ let s:noswapfile = (2 == exists(':noswapfile')) ? 'noswapfile' : '' let s:noau = 'silent noautocmd keepjumps' let s:cb_map = {} " callback map let s:rel = get(g:, 'dirvish_relative_paths', 0) +let s:virttext_feat = has('nvim-0.8') ? 'extmark' : ((v:version >= 901 && has('textprop'))? 'textprop': 'conceal') " Debug: " echo '' > dirvish.log ; tail -F dirvish.log @@ -426,9 +427,6 @@ func! s:apply_icons() abort if 0 == len(s:cb_map) return endif - if !exists('s:virttext_feat') - let s:virttext_feat = has('nvim-0.8') ? 'extmark' : ((v:version >= 901 && has('textprop'))? 'textprop': 'conceal') - endif if s:virttext_feat ==# 'extmark' if !exists('s:ns_id') let s:ns_id = nvim_create_namespace('dirvish.icons') @@ -446,29 +444,36 @@ func! s:apply_icons() abort for f in getline(1, '$') let i += 1 for id in sort(keys(s:cb_map)) - let l:icon = s:cb_map[id](f) - if type(l:icon) == v:t_string - if -1 != match(l:icon, '\S') + let icon = s:cb_map[id](f) + if type(icon) == type('') + if -1 != match(icon, '\S') + unlet icon break endif endif endfor if exists('l:icon') if s:virttext_feat ==# 'extmark' - let l:virt_text = type(l:icon) == v:t_dict ? [[l:icon.icon, l:icon.hl]] : [[l:icon, 'DirvishColumnHead']] + let l:virt_text = type(icon) == type({}) ? [[icon.icon, icon.hl]] : [[icon, 'DirvishColumnHead']] call nvim_buf_set_extmark(0, s:ns_id, i-1, 0, #{virt_text: l:virt_text, virt_text_pos: 'inline'}) elseif s:virttext_feat ==# 'textprop' - if type(l:icon) == v:t_string - call prop_add(i, 1, #{type: s:prop_type, text: l:icon}) + if type(icon) == type('') + call prop_add(i, 1, #{type: s:prop_type, text: icon}) + else + let l:prop_type = prop_type_get('dirvish.'.icon.hl, {}) + if l:prop_type == {} + call prop_type_add('dirvish.'.icon.hl, #{highlight: icon.hl}) + endif + call prop_add(i, 1, #{type: 'dirvish.'.icon.hl, text: icon.icon}) endif else let isdir = (f[-1:] == s:sep) let f = substitute(s:f(f), escape(s:sep,'\').'$', '', 'g') " Full path, trim slash. let tail_esc = escape(fnamemodify(f,':t').(isdir?(s:sep):''), '[,*.^$~\') - exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.l:icon + exe 'syntax match DirvishColumnHead =\%'.i.'l^.\{-}\ze'.tail_esc.'$= conceal cchar='.icon endif + unlet icon endif - unlet l:icon endfor endf diff --git a/doc/dirvish.txt b/doc/dirvish.txt index 56fed1b..cd2a5b2 100644 --- a/doc/dirvish.txt +++ b/doc/dirvish.txt @@ -114,12 +114,27 @@ dirvish#add_icon_fn(fn) a given path, wins. Best practice: if you don't have anything meaningful to show for a given path, return empty string (or whitespace). - {fn} is any |Funcref| that takes a path (string) and returns a string - (the "icon"). Example: >vim + {fn} is any |Funcref| that takes a path (string) and returns an `icon-item` as + a |string| or a |dict| with the following keys: + - "icon" (|string|): the string representing the icon. + - "hl" (|string|): the highlight group to use for the icon. + + Note: If your Vim isn't Vim 9.1+ with |+textprop| or Nvim 0.8+, `icon-item` + can only be a character. + + Example that returns a string: >vim call dirvish#add_icon_fn({p -> p[-1:]=='/'?'📂':'📄'}) < - Note: multi-character icons are only supported on Nvim 0.8+ or Vim 9.1+ - with |+textprop|. + Example that uses |mini.icons| + 0.15.0 as icon provider >lua + --- NOTE: mini.icons must be loaded before vim-dirvish + vim.fn['dirvish#add_icon_fn'](function(p) + local get = require('mini.icons').get + local icon, hl = get(p:sub(-1) == '/' and 'directory' or 'file', p) + icon = icon .. ' ' + return { icon = icon, hl = hl } + end) +< *dirvish#remove_icon_fn()* dirvish#remove_icon_fn(fn_id)