-
Notifications
You must be signed in to change notification settings - Fork 57
Advanced usage
Shawon edited this page Feb 8, 2025
·
3 revisions
-
Create a new file
~/.config/nvim/queries/markdown/folds.scm
. -
Add these lines to that file,
; Folds a section of the document
; that starts with a heading.
((section
(atx_heading)) @fold
(#trim! @fold))
; (#trim!) is used to prevent empty
; lines at the end of the section
; from being folded.
- To enable folding using tree-sitter only for markdown, you can use
ftplugin/
. You can do either of these 2 things.
If you don't need anything fancy, you can add this to
~/.config/nvim/ftplugin/markdown.lua
,
--- Removes the ••• part.
vim.o.fillchars = "fold: ";
vim.o.foldmethod = "expr";
vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()";
--- Disables fold text.
vim.o.foldtext = "";
If you prefer using a custom
foldtext
then you can use this instead.
---@type integer Current buffer.
local buffer = vim.api.nvim_get_current_buf();
local got_spec, spec = pcall(require, "markview.spec");
local got_util, utils = pcall(require, "markview.utils");
if got_spec == false then
--- Markview most likely not loaded.
--- no point in going forward.
return;
elseif got_util == false then
--- Same as above.
return;
end
--- Fold text creator.
---@return string
_G.heading_foldtext = function ()
--- Start & end of the current fold.
--- Note: These are 1-indexed!
---@type integer, integer
local from, to = vim.v.foldstart, vim.v.foldend;
--- Starting line
---@type string
local line = vim.api.nvim_buf_get_lines(0, from - 1, from, false)[1];
if line:match("^[%s%>]*%#+") == nil then
--- Fold didn't start on a heading.
return vim.fn.foldtext();
end
--- Heading configuration table.
---@type markdown.headings?
local main_config = spec.get({ "markdown", "headings" }, { fallback = nil });
if not main_config then
--- Headings are disabled.
return vim.fn.foldtext();
end
--- Indentation, markers & the content of a heading.
---@type string, string, string
local indent, marker, content = line:match("^([%s%>]*)(%#+)(.*)$");
--- Heading level.
---@type integer
local level = marker:len();
---@type headings.atx
local config = spec.get({ "heading_" .. level }, {
source = main_config,
fallback = nil,
--- This part isn't needed unless the user
--- does something with these parameters.
eval_args = {
buffer,
{
class = "markdown_atx_heading",
marker = marker,
text = { marker .. content },
range = {
row_start = from - 1,
row_end = from,
col_start = #indent,
col_end = #line
}
}
}
});
--- Amount of spaces to add per heading level.
---@type integer
local shift_width = spec.get({ "shift_width" }, { source = main_config, fallback = 0 });
if not config then
--- Config not found.
return vim.fn.foldtext();
elseif config.style == "simple" then
return {
{ marker .. content, utils.set_hl(config.hl) },
{
string.format(" %d", to - from),
utils.set_hl(string.format("Palette%dFg", 7 - level))
}
};
elseif config.style == "label" then
--- We won't implement alignment for the sake
--- of keeping things simple.
local shift = string.rep(" ", level * #shift_width);
return {
{ shift, utils.set_hl(config.hl) },
{ config.corner_left or "", utils.set_hl(config.corner_left_hl or config.hl) },
{ config.padding_left or "", utils.set_hl(config.padding_left_hl or config.hl) },
{ config.icon or "", utils.set_hl(config.padding_left_hl or config.hl) },
{ content:gsub("^%s", ""), utils.set_hl(config.hl) },
{ config.padding_right or "", utils.set_hl(config.padding_right_hl or config.hl) },
{ config.corner_right or "", utils.set_hl(config.corner_right_hl or config.hl) },
{
string.format(" %d", to - from),
utils.set_hl(string.format("Palette%dFg", 7 - level))
}
};
elseif config.style == "icon" then
local shift = string.rep(" ", level * shift_width);
return {
{ shift, utils.set_hl(config.hl) },
{ config.icon or "", utils.set_hl(config.padding_left_hl or config.hl) },
{ content:gsub("^%s", ""), utils.set_hl(config.hl) },
{
string.format(" %d", to - from),
utils.set_hl(string.format("Palette%dFg", 7 - level))
}
};
end
end
vim.o.fillchars = "fold: ";
vim.o.foldmethod = "expr";
vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()";
vim.o.foldtext = "v:lua.heading_foldtext()"
This has been taken from #265
Copy this to your plugin's config
option.
---@param group string New highlight group.
---@return { [string]: { hl: string } } New configuration.
local function generic_hl(group)
return {
["github%.com/[%a%d%-%_%.]+%/?$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/?$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/tree/[%a%d%-%_%.]+%/?$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+/commits/[%a%d%-%_%.]+%/?$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/releases$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/tags$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/issues$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/pulls$"] = {
hl = group
},
["github%.com/[%a%d%-%_%.]+/[%a%d%-%_%.]+%/wiki$"] = {
hl = group
}
};
end
require("markview").setup({
markdown = {
reference_definitions = generic_hl("MarkviewPalette4Fg")
},
markdown_inline = {
hyperlinks = generic_hl("MarkviewHyperlink"),
uri_autolinks = generic_hl("MarkviewEmail"),
},
typst = {
url_links = generic_hl("MarkviewEmail")
}
});
This has been taken from #283
Tip
This works the same way for Typst
too!.
Copy this to your plugin's config
option.
require("markview").setup({
markdown = {
list_items = {
shift_width = function (buffer, item)
--- Reduces the `indent` by 1 level.
---
--- indent 1
--- ------------------------- = 1 ÷ --------- = new_indent
--- indent * (1 / new_indent) new_indent
---
local parent_indnet = math.max(1, item.indent - vim.bo[buffer].shiftwidth);
return (item.indent) * (1 / (parent_indnet * 2));
end,
marker_minus = {
add_padding = function (_, item)
return item.indent > 1;
end
}
}
}
});
Also available in vimdoc, :h markview.nvim-advanced