Skip to content

Commit 5023071

Browse files
authored
diagnostics (#6)
* wip on diagnostics * add diagnostics * enable diagnostics by default
1 parent fc6e2d9 commit 5023071

File tree

4 files changed

+166
-14
lines changed

4 files changed

+166
-14
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ It will be merged with the default options, which are shown below in the example
4444
```lua
4545
require'quarto'.setup{
4646
closePreviewOnExit = true, -- close preview terminal on closing of qmd file buffer
47+
diagnostics = {
48+
enabled = true, -- enable diagnostics for embedded languages
49+
languages = {'r', 'python', 'julia'}
50+
}
4751
}
4852
```
4953

lua/quarto/init.lua

Lines changed: 147 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
local M = {}
2-
local api = vim.api
2+
local a = vim.api
3+
local q = vim.treesitter.query
34
local util = require "lspconfig.util"
45

6+
local p = function (x)
7+
print(vim.inspect(x))
8+
end
9+
510
local defaultConfig = {
611
closePreviewOnExit = true,
12+
diagnostics = {
13+
enabled = true,
14+
languages = {'r', 'python', 'julia'}
15+
}
716
}
817

9-
M.setup = function(opt)
10-
M.config = vim.tbl_deep_extend('force', defaultConfig, opt or {})
11-
end
18+
M.config = defaultConfig
19+
20+
1221

1322

1423
local function contains(list, x)
@@ -20,7 +29,7 @@ end
2029

2130
function M.quartoPreview()
2231
-- find root directory / check if it is a project
23-
local buffer_path = api.nvim_buf_get_name(0)
32+
local buffer_path = a.nvim_buf_get_name(0)
2433
local root_dir = util.root_pattern("_quarto.yml")(buffer_path)
2534
local cmd
2635
local mode
@@ -52,17 +61,145 @@ function M.quartoPreview()
5261

5362
-- close preview terminal on exit of the quarto buffer
5463
if M.config.closePreviewOnExit then
55-
api.nvim_create_autocmd({ "QuitPre", "WinClosed" }, {
56-
buffer = api.nvim_get_current_buf(),
57-
group = api.nvim_create_augroup("quartoPreview", {}),
64+
a.nvim_create_autocmd({ "QuitPre", "WinClosed" }, {
65+
buffer = a.nvim_get_current_buf(),
66+
group = a.nvim_create_augroup("quartoPreview", {}),
5867
callback = function(_, _)
59-
if api.nvim_buf_is_loaded(quartoOutputBuf) then
60-
api.nvim_buf_delete(quartoOutputBuf, { force = true })
68+
if a.nvim_buf_is_loaded(quartoOutputBuf) then
69+
a.nvim_buf_delete(quartoOutputBuf, { force = true })
6170
end
6271
end
6372
})
6473
end
6574
end
6675

6776

77+
78+
79+
-- lps support
80+
local function lines(str)
81+
local result = {}
82+
for line in str:gmatch '([^\n]*)\n?' do
83+
table.insert(result, line)
84+
end
85+
result[#result] = nil
86+
return result
87+
end
88+
89+
local function spaces(n)
90+
local s = {}
91+
for i=1,n do
92+
s[i] = ' '
93+
end
94+
return s
95+
end
96+
97+
local function get_language_content(bufnr, language)
98+
-- get and parse AST
99+
local language_tree = vim.treesitter.get_parser(bufnr, 'markdown')
100+
local syntax_tree = language_tree:parse()
101+
local root = syntax_tree[1]:root()
102+
103+
-- create capture
104+
local query = vim.treesitter.parse_query('markdown',
105+
string.gsub([[
106+
(fenced_code_block
107+
(info_string
108+
(language) @lang
109+
(#eq? @lang $language)
110+
)
111+
(code_fence_content) @code (#offset! @code)
112+
)
113+
]], "%$(%w+)", {language=language})
114+
)
115+
116+
-- get text ranges
117+
local results = {}
118+
for _, captures, metadata in query:iter_matches(root, bufnr) do
119+
local text = q.get_node_text(captures[2], bufnr)
120+
-- line numbers start at 0
121+
-- {start line, col, end line, col}
122+
local result = {
123+
range = metadata.content[1],
124+
-- text = lines(text)
125+
text = lines(text)
126+
}
127+
table.insert(results, result)
128+
end
129+
130+
return results
131+
end
132+
133+
134+
local function update_language_buffer(qmd_bufnr, language)
135+
local language_lines = get_language_content(qmd_bufnr, language)
136+
if next(language_lines) == nil then
137+
return
138+
end
139+
140+
local nmax = language_lines[#language_lines].range[3] -- last code line
141+
local qmd_path = a.nvim_buf_get_name(qmd_bufnr)
142+
local postfix
143+
if language == 'python' then
144+
postfix = '.py'
145+
elseif language == 'r' then
146+
postfix = '.R'
147+
end
148+
149+
-- create buffer filled with spaces
150+
local bufname_lang = qmd_path..postfix
151+
local bufuri_lang = 'file://'..bufname_lang
152+
local bufnr_lang = vim.uri_to_bufnr(bufuri_lang)
153+
a.nvim_buf_set_name(bufnr_lang, bufname_lang)
154+
a.nvim_buf_set_option(bufnr_lang,'filetype', language)
155+
a.nvim_buf_set_lines(bufnr_lang, 0, -1, false, {})
156+
a.nvim_buf_set_lines(bufnr_lang, 0, nmax, false, spaces(nmax))
157+
158+
-- write language lines
159+
for _,t in ipairs(language_lines) do
160+
a.nvim_buf_set_lines(bufnr_lang, t.range[1], t.range[3], false, t.text)
161+
end
162+
return bufnr_lang
163+
end
164+
165+
166+
local function enable_language_diagnostics(lang)
167+
local ns = a.nvim_create_namespace('quarto')
168+
local augroup = a.nvim_create_augroup("quartoUpdate"..lang, {})
169+
170+
a.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
171+
-- buffer = qmd_buf,
172+
pattern = '*.qmd',
173+
group = augroup,
174+
callback = function(args)
175+
local buf = update_language_buffer(0, lang)
176+
local diag = vim.diagnostic.get(buf)
177+
vim.diagnostic.reset(ns, 0)
178+
vim.diagnostic.set(ns, 0, diag, {})
179+
end
180+
})
181+
a.nvim_exec_autocmds('TextChanged', {})
182+
end
183+
184+
185+
M.enableDiagnostics = function()
186+
if M.config.diagnostics.enabled then
187+
for _,lang in ipairs(M.config.diagnostics.languages) do
188+
enable_language_diagnostics(lang)
189+
end
190+
end
191+
end
192+
193+
194+
M.setup = function(opt)
195+
M.config = vim.tbl_deep_extend('force', defaultConfig, opt or {})
196+
end
197+
198+
199+
M.debug = function()
200+
M.enableDiagnostics()
201+
a.nvim_exec_autocmds({'TextChangedI', 'TextChanged'}, {})
202+
end
203+
68204
return M
205+

plugin/quarto-nvim.lua

Lines changed: 0 additions & 4 deletions
This file was deleted.

plugin/quarto.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
local a = vim.api
2+
3+
a.nvim_create_user_command('QuartoPreview', require'quarto'.quartoPreview, {})
4+
5+
6+
a.nvim_create_autocmd({ "BufEnter" }, {
7+
pattern = '*.qmd',
8+
group = a.nvim_create_augroup('quarto', {}),
9+
callback = function(args)
10+
-- use markdown ft until quarto ft is more widespread
11+
a.nvim_buf_set_option(0,'filetype', 'markdown')
12+
require'quarto'.enableDiagnostics()
13+
end
14+
})
15+

0 commit comments

Comments
 (0)