Skip to content

Commit

Permalink
feature: Add border style to code blocks, use thin by default
Browse files Browse the repository at this point in the history
## Details

Request: MeanderingProgrammer#62

Rather than using a highlight group for the entire range of code blocks
allow half height characters to be used when start and end of code
block are entirely concealed.

This involves using the inverse of the code highlight group and
repeating the configured characters across the width.

Allow using the original implementation with `{ border = 'thick' }`.

When rendering the body we now need to be aware of how the language
was rendered, since placing a language above the code block interferes
with the half height row. To fix this we need to handle the language
from the top level code block logic, instead of using 2 separate
capture groups for code block & language.

Add sign configuration to README as well.
  • Loading branch information
MeanderingProgrammer committed Jul 14, 2024
1 parent 5ce3566 commit 3114d70
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 86 deletions.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ require('render-markdown').setup({
(thematic_break) @dash
(fenced_code_block) @code
(fenced_code_block (info_string (language) @language))
[
(list_marker_plus)
Expand Down Expand Up @@ -206,6 +205,14 @@ require('render-markdown').setup({
-- language: adds language icon to sign column and icon + name above code blocks
-- full: normal + language
style = 'full',
-- Determins how the top / bottom of code block are rendered:
-- thick: use the same highlight as the code body
-- thin: when lines are empty overlay the above & below icons
border = 'thin',
-- Used above code blocks for thin border
above = '',
-- Used below code blocks for thin border
below = '',
-- Highlight for code blocks & inline code
highlight = 'ColorColumn',
},
Expand Down Expand Up @@ -414,6 +421,14 @@ require('render-markdown').setup({
-- language: adds language icon to sign column and icon + name above code blocks
-- full: normal + language
style = 'full',
-- Determins how the top / bottom of code block are rendered:
-- thick: use the same highlight as the code body
-- thin: when lines are empty overlay the above & below icons
border = 'thin',
-- Used above code blocks for thin border
above = '',
-- Used below code blocks for thin border
below = '',
-- Highlight for code blocks & inline code
highlight = 'ColorColumn',
},
Expand Down Expand Up @@ -587,6 +602,23 @@ require('render-markdown').setup({
})
```

## Signs

```lua
require('render-markdown').setup({
sign = {
-- Turn on / off sign rendering
enabled = true,
-- More granular mechanism, disable signs within specific buftypes
exclude = {
buftypes = { 'nofile' },
},
-- Applies to background of sign text
highlight = 'SignColumn',
},
})
```

# Additional Info

- [Limitations](doc/limitations.md): Known limitations of this plugin
Expand Down
Binary file modified demo/callout.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/heading_code.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 36 additions & 2 deletions doc/render-markdown.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*render-markdown.txt* For 0.10.0 Last change: 2024 July 12
*render-markdown.txt* For 0.10.0 Last change: 2024 July 13

==============================================================================
Table of Contents *render-markdown-table-of-contents*
Expand All @@ -20,6 +20,7 @@ Table of Contents *render-markdown-table-of-contents*
- Tables |render-markdown-setup-tables|
- Callouts |render-markdown-setup-callouts|
- Links |render-markdown-setup-links|
- Signs |render-markdown-setup-signs|
7. Additional Info |render-markdown-additional-info|

==============================================================================
Expand Down Expand Up @@ -157,7 +158,6 @@ Full Default Configuration ~
(thematic_break) @dash

(fenced_code_block) @code
(fenced_code_block (info_string (language) @language))

[
(list_marker_plus)
Expand Down Expand Up @@ -243,6 +243,14 @@ Full Default Configuration ~
-- language: adds language icon to sign column and icon + name above code blocks
-- full: normal + language
style = 'full',
-- Determins how the top / bottom of code block are rendered:
-- thick: use the same highlight as the code body
-- thin: when lines are empty overlay the above & below icons
border = 'thin',
-- Used above code blocks for thin border
above = '▄',
-- Used below code blocks for thin border
below = '▀',
-- Highlight for code blocks & inline code
highlight = 'ColorColumn',
},
Expand Down Expand Up @@ -451,6 +459,14 @@ CODE BLOCKS *render-markdown-setup-code-blocks*
-- language: adds language icon to sign column and icon + name above code blocks
-- full: normal + language
style = 'full',
-- Determins how the top / bottom of code block are rendered:
-- thick: use the same highlight as the code body
-- thin: when lines are empty overlay the above & below icons
border = 'thin',
-- Used above code blocks for thin border
above = '▄',
-- Used below code blocks for thin border
below = '▀',
-- Highlight for code blocks & inline code
highlight = 'ColorColumn',
},
Expand Down Expand Up @@ -632,6 +648,24 @@ LINKS *render-markdown-setup-links*
<


SIGNS *render-markdown-setup-signs*

>lua
require('render-markdown').setup({
sign = {
-- Turn on / off sign rendering
enabled = true,
-- More granular mechanism, disable signs within specific buftypes
exclude = {
buftypes = { 'nofile' },
},
-- Applies to background of sign text
highlight = 'SignColumn',
},
})
<


==============================================================================
7. Additional Info *render-markdown-additional-info*

Expand Down
12 changes: 12 additions & 0 deletions lua/render-markdown/colors.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ M.combine = function(foreground, background)
return name
end

---@param highlight string
---@return string
M.inverse = function(highlight)
local name = string.format('RenderMd_Inverse_%s', highlight)
if not vim.tbl_contains(cache.highlights, name) then
local hl = M.get_hl(highlight)
vim.api.nvim_set_hl(0, name, { fg = hl.bg, bg = hl.fg })
table.insert(cache.highlights, name)
end
return name
end

---@private
---@param name string
---@return vim.api.keyset.hl_info
Expand Down
166 changes: 102 additions & 64 deletions lua/render-markdown/handler/markdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,82 +80,28 @@ M.render_node = function(namespace, buf, capture, node)
if not dash.enabled then
return
end
local width = vim.api.nvim_win_get_width(util.buf_to_win(buf))
vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, 0, {
virt_text = { { dash.icon:rep(width), dash.highlight } },
virt_text = { { dash.icon:rep(util.get_width(buf)), dash.highlight } },
virt_text_pos = 'overlay',
})
elseif capture == 'code' then
local code = state.config.code
if not code.enabled then
return
end
if not vim.tbl_contains({ 'normal', 'full' }, code.style) then
return
end
vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, 0, {
end_row = info.end_row,
end_col = 0,
hl_group = code.highlight,
hl_eol = true,
})
elseif capture == 'language' then
local code = state.config.code
if not code.enabled then
return
end
if not vim.tbl_contains({ 'language', 'full' }, code.style) then
return
end
local icon, icon_highlight = icons.get(info.text)
if icon == nil or icon_highlight == nil then
return
end
M.render_sign(namespace, buf, info, icon, icon_highlight)
-- Requires inline extmarks
if not util.has_10 then
return
end

local icon_text = icon .. ' '
if ts.concealed(buf, info) > 0 then
-- Fenced code blocks will pick up varying amounts of leading white space depending on
-- the context they are in. This gets lumped into the delimiter node and as a result,
-- after concealing, the extmark will be left shifted. Logic below accounts for this.
local padding = 0
local code_block = ts.parent_in_section(info.node, 'fenced_code_block')
if code_block ~= nil then
padding = str.leading_spaces(ts.info(code_block, buf).text)
end
icon_text = str.pad(icon_text .. info.text, padding)
end

local highlight = { icon_highlight }
if code.style == 'full' then
highlight = { icon_highlight, code.highlight }
end

vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, info.start_col, {
virt_text = { { icon_text, highlight } },
virt_text_pos = 'inline',
})
M.render_code(namespace, buf, info)
elseif capture == 'list_marker' then
---@return boolean
local function sibling_checkbox()
if not state.config.checkbox.enabled then
return false
end
if ts.sibling(info.node, 'task_list_marker_unchecked') ~= nil then
if ts.sibling(buf, info, 'task_list_marker_unchecked') ~= nil then
return true
end
if ts.sibling(info.node, 'task_list_marker_checked') ~= nil then
if ts.sibling(buf, info, 'task_list_marker_checked') ~= nil then
return true
end
local paragraph_node = ts.sibling(info.node, 'paragraph')
if paragraph_node == nil then
local paragraph = ts.sibling(buf, info, 'paragraph')
if paragraph == nil then
return false
end
local paragraph = ts.info(paragraph_node, buf)
return component.checkbox(paragraph.text, 'starts') ~= nil
end

Expand All @@ -175,7 +121,7 @@ M.render_node = function(namespace, buf, capture, node)
-- edge cases in the parser: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/127
-- As a result we handle leading spaces here, can remove if this gets fixed upstream
local leading_spaces = str.leading_spaces(info.text)
local level = ts.level_in_section(info.node, 'list')
local level = ts.level_in_section(info, 'list')
local icon = list.cycle(bullet.icons, level)

vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, info.start_col, {
Expand All @@ -196,9 +142,9 @@ M.render_node = function(namespace, buf, capture, node)
return
end
local highlight = quote.highlight
local quote_node = ts.parent_in_section(info.node, 'block_quote')
if quote_node ~= nil then
local callout = component.callout(ts.info(quote_node, buf).text, 'contains')
local block_quote = ts.parent_in_section(buf, info, 'block_quote')
if block_quote ~= nil then
local callout = component.callout(block_quote.text, 'contains')
if callout ~= nil then
highlight = callout.highlight
end
Expand Down Expand Up @@ -232,6 +178,98 @@ M.render_node = function(namespace, buf, capture, node)
end
end

---@param namespace integer
---@param buf integer
---@param info render.md.NodeInfo
M.render_code = function(namespace, buf, info)
local code = state.config.code
if not code.enabled then
return
end
if code.style == 'none' then
return
end
local did_render_language = false
local code_info = ts.child(buf, info, 'info_string', info.start_row)
if code_info ~= nil then
local language_info = ts.child(buf, code_info, 'language', code_info.start_row)
if language_info ~= nil then
did_render_language = M.render_language(namespace, buf, language_info, info)
end
end
if not vim.tbl_contains({ 'normal', 'full' }, code.style) then
return
end
local start_row = info.start_row
local end_row = info.end_row
-- Do not attempt to render single line code block
if start_row == end_row - 1 then
return
end
if code.border == 'thin' then
local code_start = ts.child(buf, info, 'fenced_code_block_delimiter', info.start_row)
local code_end = ts.child(buf, info, 'fenced_code_block_delimiter', info.end_row - 1)
if not did_render_language and ts.hidden(buf, code_info) and ts.hidden(buf, code_start) then
start_row = start_row + 1
vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, info.start_col, {
virt_text = { { code.above:rep(util.get_width(buf)), colors.inverse(code.highlight) } },
virt_text_pos = 'overlay',
})
end
if ts.hidden(buf, code_end) then
end_row = end_row - 1
vim.api.nvim_buf_set_extmark(buf, namespace, info.end_row - 1, info.start_col, {
virt_text = { { code.below:rep(util.get_width(buf)), colors.inverse(code.highlight) } },
virt_text_pos = 'overlay',
})
end
end
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, 0, {
end_row = end_row,
end_col = 0,
hl_group = code.highlight,
hl_eol = true,
})
end

---@param namespace integer
---@param buf integer
---@param info render.md.NodeInfo
---@param code_block render.md.NodeInfo
---@return boolean
M.render_language = function(namespace, buf, info, code_block)
local code = state.config.code
if not vim.tbl_contains({ 'language', 'full' }, code.style) then
return false
end
local icon, icon_highlight = icons.get(info.text)
if icon == nil or icon_highlight == nil then
return false
end
M.render_sign(namespace, buf, info, icon, icon_highlight)
-- Requires inline extmarks
if not util.has_10 then
return false
end
local icon_text = icon .. ' '
if ts.hidden(buf, info) then
-- Code blocks will pick up varying amounts of leading white space depending on the
-- context they are in. This gets lumped into the delimiter node and as a result,
-- after concealing, the extmark will be left shifted. Logic below accounts for this.
local padding = str.leading_spaces(code_block.text)
icon_text = str.pad(icon_text .. info.text, padding)
end
local highlight = { icon_highlight }
if code.style == 'full' then
highlight = { icon_highlight, code.highlight }
end
vim.api.nvim_buf_set_extmark(buf, namespace, info.start_row, info.start_col, {
virt_text = { { icon_text, highlight } },
virt_text_pos = 'inline',
})
return true
end

---@param namespace integer
---@param buf integer
---@param info render.md.NodeInfo
Expand Down
Loading

0 comments on commit 3114d70

Please sign in to comment.