summaryrefslogtreecommitdiffstats
path: root/.config/nvim/after/ftplugin/markdown.lua
blob: f0f969100f4b8c747c980214d25bb76684050735 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
vim.opt_local.tabstop = 2 -- CommonMark expects two spaces for indentation
vim.opt_local.shiftwidth = 0
vim.opt_local.softtabstop = -1
vim.opt_local.expandtab = true -- Change tabs to spaces
-- Nothing in the gutter, except sign columns if necessary
vim.opt_local.number = false
vim.opt_local.relativenumber = false
vim.opt_local.signcolumn = "auto"
vim.opt_local.foldcolumn = "0"
--
vim.opt_local.textwidth = vim.g.dotfiles.textwidth.markdown -- gw wraps at this value
-- There is a bug where folded fenced code blocks will be completely invisible
-- But (apparently) it only happen when there is a # heading (and no ## heading, no ### heading etc)
-- Probably caused by tree-sitter parsing, but it's not really a problem, since adding a ## heading fixes it
-- Just remember this and don't try to fix it again!
vim.opt_local.foldlevel = 1
vim.opt_local.complete = {
	"o", -- 'omnifunc'
	".", -- current buffer
}
-- Visible link labels
vim.api.nvim_set_hl(0, "@markup.link.label.markdown_inline", { underline = true, update = true })

vim.b.format = true -- See plugin/50-format.lua

local function item_range()
	local cursor_row = vim.api.nvim_win_get_cursor(0)[1]
	local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)

	-- Walk up to the task bullet. Any other bullet, or a line that's neither
	-- blank nor indented, means we're not inside a task item.
	local start_row = cursor_row
	while start_row >= 1 do
		local line = lines[start_row]
		if line:match("^%s*%- %[[ Xx]%] ") then
			break
		end
		if line:match("^%s*%- ") then
			return nil
		end
		if not (line:match("^%s*$") or line:match("^%s+%S")) then
			return nil
		end
		start_row = start_row - 1
	end
	if start_row < 1 then
		return nil
	end

	-- Walk down through continuation paragraphs. Blank lines are inside the
	-- item as long as a later line is still indented to the content column.
	local bullet_indent = #lines[start_row]:match("^(%s*)")
	local cont_min = bullet_indent + 2
	local end_row = start_row
	for r = start_row + 1, #lines do
		local line = lines[r]
		if line:match("^%s*$") then
			-- blank, keep scanning
		elseif line:match("^%s*%- ") then
			break
		elseif #line:match("^(%s*)") < cont_min then
			break
		else
			end_row = r
		end
	end

	return start_row, end_row
end

local function check_checklist_item()
	local start_row, end_row = item_range()
	if not (start_row and end_row) then
		return
	end
	local lines = vim.api.nvim_buf_get_lines(0, start_row - 1, end_row, false)
	if not lines[1]:match("^%s*%- %[ %] ") then
		return
	end
	for i, line in ipairs(lines) do
		if i == 1 then
			lines[i] = line:gsub("^(%s*%- )%[ %] (.-)(%s*\\?)$", "%1[X] ~%2~%3", 1)
		elseif not line:match("^%s*$") then
			lines[i] = line:gsub("^(%s*)(.-)(%s*\\?)$", "%1~%2~%3", 1)
		end
	end
	vim.api.nvim_buf_set_lines(0, start_row - 1, end_row, false, lines)
end

local function uncheck_checklist_item()
	local start_row, end_row = item_range()
	if not (start_row and end_row) then
		return
	end
	local lines = vim.api.nvim_buf_get_lines(0, start_row - 1, end_row, false)
	if not lines[1]:match("^%s*%- %[X%] ") then
		return
	end
	for i, line in ipairs(lines) do
		if i == 1 then
			lines[i] = line:gsub("%[X%]", "[ ]", 1):gsub("~", "", 2)
		elseif not line:match("^%s*$") then
			lines[i] = line:gsub("~", "", 2)
		end
	end
	vim.api.nvim_buf_set_lines(0, start_row - 1, end_row, false, lines)
end

vim.api.nvim_buf_create_user_command(0, "MarkdownCheck", check_checklist_item, { desc = "Check checklist item" })
vim.api.nvim_buf_create_user_command(0, "MarkdownUncheck", uncheck_checklist_item, { desc = "Uncheck checklist item" })