Skip to content

Commit dfffdd2

Browse files
fix: more robust icon provider resolution
## Details Issue: #403 The `get` call into `mini.icons` can sometimes be `nil`, while I'm unable to replicate it we can definitely make this module more robust by adding more checks than a `pcall` around `require`. To fix this the logic has been entirely re-written. In addition to `pcall` we now also: - validate the required module is not `nil` - validate that the method we use for the provider is not `nil` - validate `_G.MiniIcons ~= nil` as specified in the docs Minor other changes: - pull `default` configuration directly in state module where needed - add an `alias` for `dash` width type - consistently call cache tables `Cache` and use `class` annotation - `render.md.BufferConfig` -> `render.md.main.Config` - directly invalidate state cache in `init` module
1 parent dfc1299 commit dfffdd2

File tree

22 files changed

+176
-121
lines changed

22 files changed

+176
-121
lines changed

doc/render-markdown.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For 0.11.0 Last change: 2025 April 17
1+
*render-markdown.txt* For 0.11.0 Last change: 2025 April 21
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*

lua/render-markdown/api.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ function M.debug()
5454
end
5555

5656
function M.config()
57-
local markdown = require('render-markdown')
58-
local difference = state.difference(markdown.default)
57+
local difference = state.difference()
5958
if vim.tbl_count(difference) == 0 then
6059
vim.print('Default Configuration')
6160
else

lua/render-markdown/colors.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
---@class render.md.cache.Colors
1+
---@class render.md.colors.Cache
22
---@field combine table<string, { fg: string, bg: string }>
33
---@field bg_to_fg table<string, { hl: string }>
44
local Cache = {

lua/render-markdown/config.lua

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
local Env = require('render-markdown.lib.env')
22
local Range = require('render-markdown.core.range')
33

4-
---@class render.md.Components
4+
---@class render.md.main.Components
55
---@field callout table<string, render.md.callout.Config>
66
---@field checkbox table<string, render.md.checkbox.custom.Config>
77

8-
---@class render.md.BufferConfig: render.md.buffer.Config
8+
---@class render.md.main.Config: render.md.buffer.Config
99
---@field private modes render.md.Modes
10-
---@field private components render.md.Components
10+
---@field private components render.md.main.Components
1111
local Config = {}
1212
Config.__index = Config
1313

1414
---@param config render.md.buffer.Config
15-
---@return render.md.BufferConfig
15+
---@return render.md.main.Config
1616
function Config.new(config)
1717
-- Super set of render modes across top level and individual components
1818
local modes = config.render_modes
@@ -22,7 +22,7 @@ function Config.new(config)
2222
end
2323
end
2424

25-
---@type render.md.Components
25+
---@type render.md.main.Components
2626
local components = {
2727
callout = Config.normalize(config.callout),
2828
checkbox = Config.normalize(config.checkbox.custom),
@@ -119,19 +119,17 @@ end
119119
---@param row? integer
120120
---@return render.md.Range?
121121
function Config:hidden(mode, row)
122-
-- Anti-conceal is not enabled -> hide nothing
123-
-- Row is not known -> buffer is not active -> hide nothing
124-
if not self.anti_conceal.enabled or row == nil then
122+
-- anti-conceal is not enabled -> hide nothing
123+
-- row is not known -> buffer is not active -> hide nothing
124+
local config = self.anti_conceal
125+
if not config.enabled or row == nil then
125126
return nil
126127
end
127128
if vim.tbl_contains({ 'v', 'V', '\22' }, mode) then
128129
local start = vim.fn.getpos('v')[2] - 1
129130
return Range.new(math.min(row, start), math.max(row, start))
130131
else
131-
return Range.new(
132-
row - self.anti_conceal.above,
133-
row + self.anti_conceal.below
134-
)
132+
return Range.new(row - config.above, row + config.below)
135133
end
136134
end
137135

lua/render-markdown/config/dash.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
---@class (exact) render.md.dash.Config: render.md.base.Config
22
---@field icon string
3-
---@field width 'full'|number
3+
---@field width render.md.dash.Width
44
---@field left_margin number
55
---@field highlight string
66

7+
---@alias render.md.dash.Width 'full'|number
8+
79
local M = {}
810

911
---@param spec render.md.debug.ValidatorSpec

lua/render-markdown/core/context.lua

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,29 +220,29 @@ function Context:for_each(callback)
220220
return false
221221
end
222222

223-
---@type table<integer, render.md.Context>
224-
local cache = {}
223+
---@class render.md.context.Cache: { [integer]: render.md.Context }
224+
local Cache = {}
225225

226-
---@class render.md.ContextManager
226+
---@class render.md.context.Manager
227227
local M = {}
228228

229229
---@param props render.md.context.Props
230230
function M.reset(props)
231-
cache[props.buf] = Context.new(props, 10)
231+
Cache[props.buf] = Context.new(props, 10)
232232
end
233233

234234
---@param buf integer
235235
---@param win integer
236236
---@return boolean
237237
function M.contains_range(buf, win)
238-
local context = cache[buf]
238+
local context = Cache[buf]
239239
return context ~= nil and context:contains_window(win)
240240
end
241241

242242
---@param buf integer
243243
---@return render.md.Context
244244
function M.get(buf)
245-
return cache[buf]
245+
return Cache[buf]
246246
end
247247

248248
return M

lua/render-markdown/core/log.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ local Env = require('render-markdown.lib.env')
1212
---@field private file string
1313
local M = {}
1414

15+
---Should only be called from state on setup
1516
---@param level render.md.config.LogLevel
1617
function M.setup(level)
1718
-- Write out any logs before closing

lua/render-markdown/core/ui.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ local builtin_handlers = {
2323
markdown_inline = require('render-markdown.handler.markdown_inline'),
2424
}
2525

26-
---@class render.md.cache.Ui
26+
---@class render.md.ui.Cache
2727
---@field states table<integer, render.md.Buffer>
2828
local Cache = {
2929
states = {},
@@ -97,7 +97,8 @@ function M.update(buf, win, event, change)
9797
end
9898

9999
local parse = M.parse(buf, win, change)
100-
local config, buffer = state.get(buf), Cache.get(buf)
100+
local config = state.get(buf)
101+
local buffer = Cache.get(buf)
101102
if buffer:is_empty() then
102103
return
103104
end
@@ -181,7 +182,7 @@ function M.run_update(buf, win, change)
181182
end
182183

183184
---@private
184-
---@param config render.md.BufferConfig
185+
---@param config render.md.main.Config
185186
---@param win integer
186187
---@param mode string
187188
---@return 'default'|'rendered'

lua/render-markdown/handler/html.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local state = require('render-markdown.state')
44
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.Html
7-
---@field private config render.md.BufferConfig
7+
---@field private config render.md.main.Config
88
---@field private context render.md.Context
99
---@field private marks render.md.Marks
1010
---@field private query vim.treesitter.Query

lua/render-markdown/handler/latex.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ local Str = require('render-markdown.lib.str')
66
local log = require('render-markdown.core.log')
77
local state = require('render-markdown.state')
88

9-
---@type table<string, string>
10-
local cache = {}
9+
---@class render.md.latex.Cache: { [string]: string }
10+
local Cache = {}
1111

1212
---@class render.md.handler.Latex: render.md.Handler
1313
local M = {}
@@ -27,14 +27,14 @@ function M.parse(ctx)
2727
local node = Node.new(ctx.buf, ctx.root)
2828
log.node('latex', node)
2929

30-
local raw_expression = cache[node.text]
30+
local raw_expression = Cache[node.text]
3131
if raw_expression == nil then
3232
raw_expression = vim.fn.system(latex.converter, node.text)
3333
if vim.v.shell_error == 1 then
3434
log.add('error', latex.converter, raw_expression)
3535
raw_expression = 'error'
3636
end
37-
cache[node.text] = raw_expression
37+
Cache[node.text] = raw_expression
3838
end
3939

4040
local expressions = {} ---@type string[]

lua/render-markdown/handler/markdown.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local state = require('render-markdown.state')
44
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.Markdown
7-
---@field private config render.md.BufferConfig
7+
---@field private config render.md.main.Config
88
---@field private context render.md.Context
99
---@field private marks render.md.Marks
1010
---@field private query vim.treesitter.Query

lua/render-markdown/handler/markdown_inline.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local state = require('render-markdown.state')
44
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.MarkdownInline
7-
---@field private config render.md.BufferConfig
7+
---@field private config render.md.main.Config
88
---@field private context render.md.Context
99
---@field private marks render.md.Marks
1010
---@field private query vim.treesitter.Query

lua/render-markdown/health.lua

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
local Icons = require('render-markdown.lib.icons')
1+
local Icons = require('render-markdown.integ.icons')
22
local state = require('render-markdown.state')
33

44
---@class render.md.Health
55
local M = {}
66

77
---@private
8-
M.version = '8.3.1'
8+
M.version = '8.3.2'
99

1010
function M.check()
1111
M.start('version')
@@ -33,7 +33,7 @@ function M.check()
3333
M.check_highlight('markdown')
3434

3535
M.start('icons')
36-
local provider = Icons.provider()
36+
local provider = Icons.name()
3737
if provider ~= nil then
3838
vim.health.ok('using: ' .. provider)
3939
else
@@ -65,17 +65,15 @@ function M.start(name)
6565
end
6666

6767
---@private
68-
---@param minimum string
69-
---@param recommended string
70-
function M.neovim(minimum, recommended)
71-
if vim.fn.has('nvim-' .. minimum) == 0 then
72-
vim.health.error('neovim < ' .. minimum)
73-
elseif vim.fn.has('nvim-' .. recommended) == 0 then
74-
vim.health.warn(
75-
'neovim < ' .. recommended .. ' some features will not work'
76-
)
68+
---@param min string
69+
---@param rec string
70+
function M.neovim(min, rec)
71+
if vim.fn.has('nvim-' .. min) == 0 then
72+
vim.health.error('neovim < ' .. min)
73+
elseif vim.fn.has('nvim-' .. rec) == 0 then
74+
vim.health.warn('neovim < ' .. rec .. ' some features will not work')
7775
else
78-
vim.health.ok('neovim >= ' .. recommended)
76+
vim.health.ok('neovim >= ' .. rec)
7977
end
8078
end
8179

lua/render-markdown/init.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,8 @@ function M.setup(opts)
714714
end
715715
M.initialized = true
716716

717-
require('render-markdown.state').setup(M.default, opts or {})
717+
require('render-markdown.state').setup(opts or {})
718+
require('render-markdown.state').invalidate_cache()
718719
require('render-markdown.core.ui').invalidate_cache()
719720
end
720721

lua/render-markdown/integ/icons.lua

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---@class render.md.icon.Provider
2+
---@field name? string
3+
---@field get fun(filetype: string): string?, string?
4+
5+
---@class render.md.icon.Providers
6+
Providers = {}
7+
8+
---@return render.md.icon.Provider?
9+
function Providers.MiniIcons()
10+
local has, icons = pcall(require, 'mini.icons')
11+
if not has or icons == nil then
12+
return nil
13+
end
14+
local getter = icons.get
15+
if getter == nil then
16+
return nil
17+
end
18+
-- additional check recommended by author
19+
if _G.MiniIcons == nil then
20+
return nil
21+
end
22+
---@type render.md.icon.Provider
23+
return {
24+
name = 'mini.icons',
25+
get = function(filetype)
26+
return getter('filetype', filetype)
27+
end,
28+
}
29+
end
30+
31+
---@return render.md.icon.Provider?
32+
function Providers.DevIcons()
33+
local has, icons = pcall(require, 'nvim-web-devicons')
34+
if not has or icons == nil then
35+
return nil
36+
end
37+
local getter = icons.get_icon_by_filetype
38+
if getter == nil then
39+
return nil
40+
end
41+
---@type render.md.icon.Provider
42+
return {
43+
name = 'nvim-web-devicons',
44+
get = function(filetype)
45+
return getter(filetype)
46+
end,
47+
}
48+
end
49+
50+
---@return render.md.icon.Provider
51+
function Providers.None()
52+
---@type render.md.icon.Provider
53+
return {
54+
name = nil,
55+
get = function()
56+
return nil, nil
57+
end,
58+
}
59+
end
60+
61+
---@class render.md.integ.Icons
62+
---@field private provider? render.md.icon.Provider
63+
local M = {}
64+
65+
---@return string?
66+
function M.name()
67+
return M.resolve().name
68+
end
69+
70+
---@param name string
71+
---@return string?, string?
72+
function M.get(name)
73+
-- handle input possibly being an extension rather than a language name
74+
local filetype = vim.filetype.match({ filename = 'a.' .. name }) or name
75+
return M.resolve().get(filetype)
76+
end
77+
78+
---@private
79+
---@return render.md.icon.Provider
80+
function M.resolve()
81+
local provider = M.provider
82+
if provider == nil then
83+
provider = provider or Providers.MiniIcons()
84+
provider = provider or Providers.DevIcons()
85+
provider = provider or Providers.None()
86+
M.provider = provider
87+
end
88+
return provider
89+
end
90+
91+
return M

lua/render-markdown/integ/source.lua

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,13 @@ end
106106
---@return boolean
107107
function M.ignore(marker, text)
108108
local prefix = vim.pesc(vim.trim(marker)) .. '%s+'
109-
local patterns = {
110-
-- The first non-space after the marker is not '['
111-
prefix .. '[^%[]',
112-
-- After '[' there is another '[' or a space
113-
prefix .. '%[.*[%[%s]',
114-
-- There is already text enclosed by '[' ']'
115-
prefix .. '%[.*%]',
116-
}
109+
local patterns = {} ---@type string[]
110+
-- first non-space after the marker is not '['
111+
patterns[#patterns + 1] = prefix .. '[^%[]'
112+
-- after '[' there is another '[' or a space
113+
patterns[#patterns + 1] = prefix .. '%[.*[%[%s]'
114+
-- there is already text enclosed by '[' ']'
115+
patterns[#patterns + 1] = prefix .. '%[.*%]'
117116
for _, pattern in ipairs(patterns) do
118117
if text:find(pattern) ~= nil then
119118
return true

0 commit comments

Comments
 (0)