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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
local function format()
local view = vim.fn.winsaveview()
local buf_str = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), "\n") .. "\n"
local tempname = vim.fn.tempname()
local tempfile = assert(io.open(tempname, "w"), "Could not open temporary file")
tempfile:write(buf_str)
tempfile:close()
local r_check = vim.system({
"mdformat",
"--number",
"--extensions",
"tables",
"--extensions",
"frontmatter",
"--extensions",
"wikilink",
"--wrap",
tostring(vim.bo.textwidth),
"--check",
tempname,
}):wait()
if r_check.code == 0 then
return
elseif r_check.code ~= 1 then
vim.notify("mdformat failed (" .. r_check.code .. "):\n" .. r_check.stderr)
end
local r_format = vim.system({
"mdformat",
"--number",
"--extensions",
"tables",
"--extensions",
"frontmatter",
"--extensions",
"wikilink",
"--wrap",
tostring(vim.bo.textwidth),
tempname,
}):wait()
if r_format.code ~= 0 then
vim.notify("mdformat failed (" .. r_format.code .. "):\n" .. r_format.stderr)
end
local formatted_lines = {}
for line in io.lines(tempname) do
formatted_lines[#formatted_lines + 1] = line
end
os.remove(tempname)
vim.api.nvim_buf_set_lines(0, 0, -1, false, formatted_lines)
vim.fn.winrestview(view)
end
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.autoformat = true
vim.b.format_func = format
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" })
|