Skip to content

Commit d5e79fb

Browse files
committed
treesitter
1 parent 92e0f61 commit d5e79fb

File tree

4 files changed

+123
-2
lines changed

4 files changed

+123
-2
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ require("quicker").setup({
198198
-- Keep the cursor to the right of the filename and lnum columns
199199
constrain_cursor = true,
200200
highlight = {
201-
-- Use treesitter highlighting
201+
-- Attch parser to qf buffer, highlight text in real time as you edit.
202+
-- If this is true, other highlight options are ignored.
203+
attach_parser = true,
204+
-- Query treesitter highlight groups using string parser, only update after you save.
202205
treesitter = true,
203206
-- Use LSP semantic token highlighting
204207
lsp = true,

lua/quicker/config.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ end
154154
---@field soft_end? string
155155

156156
---@class (exact) quicker.HighlightConfig
157+
---@field attach_parser boolean
157158
---@field treesitter boolean
158159
---@field lsp boolean
159160
---@field load_buffers boolean

lua/quicker/display.lua

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ local function highlight_buffer_when_entered(qfbufnr, info)
267267
vim.b[qfbufnr].pending_highlight = nil
268268
info.start_idx = 1
269269
info.end_idx = vim.api.nvim_buf_line_count(qfbufnr)
270+
info.regions = {}
270271
schedule_highlights(info)
271272
end,
272273
})
@@ -339,7 +340,26 @@ add_qf_highlights = function(info)
339340
loaded = true
340341
end
341342

342-
if loaded then
343+
-- If attach_parser is true, we should not apply lsp highlight or query ts highlights
344+
-- from loaded buffers, as they will overwrite each other.
345+
if config.highlight.attach_parser then
346+
local filename = vim.split(line, EM_QUAD, { plain = true })[1]
347+
local ft = vim.filetype.match({ buf = item.bufnr })
348+
if ft == nil then
349+
goto continue
350+
end
351+
if info.regions[ft] == nil then
352+
info.regions[ft] = {}
353+
end
354+
table.insert(info.regions[ft], {
355+
{
356+
i - 1,
357+
filename:len() + EM_QUAD_LEN - 1, -- skip EM_QUAD
358+
i - 1,
359+
#line,
360+
},
361+
})
362+
elseif loaded then
343363
add_item_highlights_from_buf(qfbufnr, item, line, i)
344364
elseif config.highlight.treesitter then
345365
local filename = vim.split(line, EM_QUAD, { plain = true })[1]
@@ -382,6 +402,10 @@ add_qf_highlights = function(info)
382402
schedule_highlights(info)
383403
return
384404
end
405+
::continue::
406+
end
407+
if config.highlight.attach_parser then
408+
require("quicker.treesitter").attach(qf_list.qfbufnr, info.regions)
385409
end
386410

387411
vim.api.nvim_buf_clear_namespace(qf_list.qfbufnr, ns, info.end_idx, -1)
@@ -425,6 +449,7 @@ end
425449
---@field id integer
426450
---@field start_idx integer
427451
---@field end_idx integer
452+
---@field regions table<string, number[][][]>
428453
---@field winid integer
429454
---@field quickfix 1|0
430455
---@field force_bufload? boolean field injected by us to control if we're forcing a bufload for the syntax highlighting
@@ -588,6 +613,7 @@ function M.quickfixtextfunc(info)
588613

589614
-- If we just rendered the last item, add highlights
590615
if info.end_idx == #items then
616+
info.regions = {}
591617
schedule_highlights(info)
592618

593619
if qf_list.qfbufnr > 0 then

lua/quicker/treesitter.lua

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---@alias trouble.LangRegions table<string, number[][][]>
2+
3+
local M = {}
4+
5+
M.cache = {} ---@type table<number, table<string,{parser: vim.treesitter.LanguageTree, highlighter:vim.treesitter.highlighter, enabled:boolean}>>
6+
local ns = vim.api.nvim_create_namespace("quicker.treesitter")
7+
8+
local TSHighlighter = vim.treesitter.highlighter
9+
10+
local function wrap(name)
11+
return function(_, win, buf, ...)
12+
if not M.cache[buf] then
13+
return false
14+
end
15+
for _, hl in pairs(M.cache[buf] or {}) do
16+
if hl.enabled then
17+
TSHighlighter.active[buf] = hl.highlighter
18+
TSHighlighter[name](_, win, buf, ...)
19+
end
20+
end
21+
TSHighlighter.active[buf] = nil
22+
end
23+
end
24+
25+
M.did_setup = false
26+
function M.setup()
27+
if M.did_setup then
28+
return
29+
end
30+
M.did_setup = true
31+
32+
vim.api.nvim_set_decoration_provider(ns, {
33+
on_win = wrap("_on_win"),
34+
on_line = wrap("_on_line"),
35+
})
36+
37+
vim.api.nvim_create_autocmd("QuickFixCmdPost", {
38+
group = vim.api.nvim_create_augroup("quicker.treesitter.hl", { clear = true }),
39+
callback = function(ev)
40+
M.cache[ev.buf] = nil
41+
end,
42+
})
43+
end
44+
45+
---@param buf number
46+
---@param regions trouble.LangRegions
47+
function M.attach(buf, regions)
48+
M.setup()
49+
M.cache[buf] = M.cache[buf] or {}
50+
for lang in pairs(M.cache[buf]) do
51+
M.cache[buf][lang].enabled = regions[lang] ~= nil
52+
end
53+
54+
for lang in pairs(regions) do
55+
M._attach_lang(buf, lang, regions[lang])
56+
end
57+
end
58+
59+
---@param buf number
60+
---@param lang? string
61+
function M._attach_lang(buf, lang, regions)
62+
lang = lang or "markdown"
63+
lang = lang == "markdown" and "markdown_inline" or lang
64+
65+
M.cache[buf] = M.cache[buf] or {}
66+
67+
if not M.cache[buf][lang] then
68+
local ok, parser = pcall(vim.treesitter.get_parser, buf, lang)
69+
if not ok then
70+
return
71+
end
72+
parser:set_included_regions(vim.deepcopy(regions))
73+
M.cache[buf][lang] = {
74+
parser = parser,
75+
highlighter = TSHighlighter.new(parser),
76+
}
77+
end
78+
M.cache[buf][lang].enabled = true
79+
local parser = M.cache[buf][lang].parser
80+
81+
parser:set_included_regions(vim.deepcopy(regions))
82+
-- Run a full parse for all included regions. There are two reasons:
83+
-- 1. When we call `vim.treesitter.get_parser`, we have not set any
84+
-- injection ranges.
85+
-- 2. If this is not called, the highlighter will do incremental parsing,
86+
-- which means it only parses visible areas (the on_win and on_line callback),
87+
-- so if we modify the buffer, unvisited area's state get unsynced.
88+
parser:parse(true)
89+
end
90+
91+
return M

0 commit comments

Comments
 (0)