Skip to content

Commit 26bbd94

Browse files
authored
Merge pull request #32 from AuroBreeze/dev
Dev
2 parents 5b0ec45 + 2fe48b6 commit 26bbd94

File tree

4 files changed

+135
-76
lines changed

4 files changed

+135
-76
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
<img src="https://dotfyle.com/plugins/AuroBreeze/quick-c/shield" />
3232
</a>
3333

34+
<a href="https://deepwiki.com/AuroBreeze/quick-c"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
35+
3436
## ✨ 特性
3537

3638
- 🚀 **一键构建/运行(异步)**`QuickCBuild``QuickCRun``QuickCBR`(构建并运行)

Release.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Quick-c Release Notes
22

3+
## v1.5.14 (2025-11-27)
4+
5+
### 修复
6+
- 构建失败后弹出 quickfix,随后再次使用 `cqb` 无法构建(队列似乎“卡住”)的问题:
7+
- 将单/多文件构建路径中 `jobstart(...).on_exit` 的完整逻辑封装到 `pcall` 内。
8+
- 诊断解析、设置 quickfix、打开 quickfix/Telescope、日志写入等步骤全部加上保护;无论是否出错,最终都会调用 `done(code)` 释放任务队列。
9+
10+
### 影响范围
11+
- 版本:v1.5.8 及之后的任务队列版本均可能受影响。
12+
- 触发条件:构建失败且在 on_exit 阶段出现未捕获异常(例如外部插件/环境差异导致的 `require('telescope')``setqflist`、窗口选项设置异常等)。
13+
14+
### 根因
15+
- on_exit 回调中某些分支未出现异常时表现正常;但一旦抛错,`done(code)` 未被执行,队列保持“进行中”,后续 `cqb`/`cqR` 不再被调度。
16+
17+
### 验证建议
18+
1) 制造一个会编译失败的示例,执行 `<leader>cqb`,确认 quickfix 正常弹出;
19+
2) 立即再次执行 `<leader>cqb``:QuickCBuild`,应能正常开始新的构建;
20+
3) 在安装/未安装 Telescope 的环境分别验证,均不应再出现“无法再次构建”。
21+
22+
### 兼容性
23+
- 无破坏性变更;无需迁移或修改配置。
24+
325
## v1.5.13 (2025-11-06) Hotfix
426

527
### 修复

lua/quick-c/build.lua

Lines changed: 81 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -629,93 +629,98 @@ function B.build(config, notify, opts)
629629
end
630630
end,
631631
on_exit = function(_, code)
632-
local diagcfg = (config.diagnostics and config.diagnostics.quickfix) or {}
633-
local qf_enabled = (diagcfg.enabled ~= false)
634-
local lines = {}
635-
for _, s in ipairs(all_stdout) do table.insert(lines, s) end
636-
for _, s in ipairs(all_stderr) do table.insert(lines, s) end
637-
if #lines > 0 then write_build_logs(lines) end
638-
-- Compute diagnostics summary for notification
639-
local items = {}
640-
do
641-
local parsed = { U.parse_diagnostics(lines) }
642-
items = parsed[1] or {}
643-
end
632+
-- Ensure any unexpected error here won't block the task queue
644633
local err_cnt, warn_cnt = 0, 0
645-
for _, it in ipairs(items) do
646-
if it.type == 'E' then err_cnt = err_cnt + 1 else warn_cnt = warn_cnt + 1 end
647-
end
648-
if qf_enabled and #lines > 0 then
649-
local items, has_error, has_warning = U.parse_diagnostics(lines)
650-
if #items > 0 then
651-
vim.fn.setqflist({}, ' ', { title = 'Quick-c Build', items = items })
652-
local function should_open()
653-
if diagcfg.open == 'always' then return true end
654-
if diagcfg.open == 'error' and has_error then return true end
655-
if diagcfg.open == 'warning' and (has_error or has_warning) then return true end
656-
return false
657-
end
658-
local function should_jump()
659-
if diagcfg.jump == 'always' then return true end
660-
if diagcfg.jump == 'error' and has_error then return true end
661-
if diagcfg.jump == 'warning' and (has_error or has_warning) then return true end
662-
return false
634+
local ok = pcall(function()
635+
local diagcfg = (config.diagnostics and config.diagnostics.quickfix) or {}
636+
local qf_enabled = (diagcfg.enabled ~= false)
637+
local lines = {}
638+
for _, s in ipairs(all_stdout) do table.insert(lines, s) end
639+
for _, s in ipairs(all_stderr) do table.insert(lines, s) end
640+
if #lines > 0 then pcall(write_build_logs, lines) end
641+
-- Compute diagnostics summary for notification (protected)
642+
local parsed_items = {}
643+
local ok_parse, pitems, has_error, has_warning = pcall(U.parse_diagnostics, lines)
644+
if ok_parse then
645+
parsed_items = pitems or {}
646+
for _, it in ipairs(parsed_items) do
647+
if it.type == 'E' then err_cnt = err_cnt + 1 else warn_cnt = warn_cnt + 1 end
663648
end
664-
if should_open() then
665-
if diagcfg.use_telescope then
666-
local ok_qc, qct = pcall(require, 'quick-c.telescope')
667-
if ok_qc and qct and qct.telescope_quickfix then
668-
pcall(qct.telescope_quickfix, config)
669-
else
670-
local ok_tb, tb = pcall(require, 'telescope.builtin')
671-
if ok_tb and tb and tb.quickfix then
672-
pcall(tb.quickfix)
649+
else
650+
parsed_items = {}
651+
err_cnt, warn_cnt = 0, 0
652+
end
653+
if qf_enabled and #lines > 0 then
654+
if ok_parse and #parsed_items > 0 then
655+
pcall(vim.fn.setqflist, {}, ' ', { title = 'Quick-c Build', items = parsed_items })
656+
local function should_open()
657+
if diagcfg.open == 'always' then return true end
658+
if diagcfg.open == 'error' and has_error then return true end
659+
if diagcfg.open == 'warning' and (has_error or has_warning) then return true end
660+
return false
661+
end
662+
local function should_jump()
663+
if diagcfg.jump == 'always' then return true end
664+
if diagcfg.jump == 'error' and has_error then return true end
665+
if diagcfg.jump == 'warning' and (has_error or has_warning) then return true end
666+
return false
667+
end
668+
if should_open() then
669+
if diagcfg.use_telescope then
670+
local ok_qc, qct = pcall(require, 'quick-c.telescope')
671+
if ok_qc and qct and qct.telescope_quickfix then
672+
pcall(qct.telescope_quickfix, config)
673673
else
674-
pcall(vim.cmd, 'copen')
674+
local ok_tb, tb = pcall(require, 'telescope.builtin')
675+
if ok_tb and tb and tb.quickfix then
676+
pcall(tb.quickfix)
677+
else
678+
pcall(vim.cmd, 'copen')
679+
end
675680
end
681+
else
682+
pcall(vim.cmd, 'copen')
676683
end
677-
else
678-
pcall(vim.cmd, 'copen')
679-
end
680-
if not (pcall(require, 'telescope')) then
681-
local info = vim.fn.getqflist({ winid = 1 }) or {}
682-
local wid = info.winid or 0
683-
if wid ~= 0 then
684-
pcall(vim.api.nvim_win_set_option, wid, 'wrap', true)
685-
pcall(vim.api.nvim_win_set_option, wid, 'linebreak', true)
686-
pcall(vim.api.nvim_win_set_option, wid, 'breakindent', true)
684+
if not (pcall(require, 'telescope')) then
685+
local info = vim.fn.getqflist({ winid = 1 }) or {}
686+
local wid = info.winid or 0
687+
if wid ~= 0 then
688+
pcall(vim.api.nvim_win_set_option, wid, 'wrap', true)
689+
pcall(vim.api.nvim_win_set_option, wid, 'linebreak', true)
690+
pcall(vim.api.nvim_win_set_option, wid, 'breakindent', true)
691+
end
687692
end
688693
end
689-
end
690-
if should_jump() then
691-
local cur = vim.api.nvim_get_current_buf()
692-
local nameb = vim.api.nvim_buf_get_name(cur)
693-
local modified = false
694-
pcall(function() modified = vim.api.nvim_buf_get_option(cur, 'modified') end)
695-
if not (nameb == '' and modified) then
696-
pcall(vim.cmd, 'silent! keepalt keepjumps cc')
694+
if should_jump() then
695+
local cur = vim.api.nvim_get_current_buf()
696+
local nameb = vim.api.nvim_buf_get_name(cur)
697+
local modified = false
698+
pcall(function() modified = vim.api.nvim_buf_get_option(cur, 'modified') end)
699+
if not (nameb == '' and modified) then
700+
pcall(vim.cmd, 'silent! keepalt keepjumps cc')
701+
end
697702
end
703+
else
704+
if code == 0 then pcall(vim.fn.setqflist, {}) end
698705
end
706+
end
707+
local dur = (((vim.loop and vim.loop.now and vim.loop.now()) or started_at) - started_at)
708+
local secs = (dur or 0) / 1000
709+
local msg = {}
710+
if code == 0 then
711+
table.insert(msg, string.format('Build OK -> %s', exe))
699712
else
700-
if code == 0 then vim.fn.setqflist {} end
713+
table.insert(msg, string.format('Build failed (%d)', code))
701714
end
702-
end
703-
local dur = (((vim.loop and vim.loop.now and vim.loop.now()) or started_at) - started_at)
704-
local secs = (dur or 0) / 1000
705-
local msg = {}
706-
if code == 0 then
707-
table.insert(msg, string.format('Build OK -> %s', exe))
708-
else
709-
table.insert(msg, string.format('Build failed (%d)', code))
710-
end
711-
table.insert(msg, string.format('Time: %.2fs | Errors: %d, Warnings: %d', secs, err_cnt, warn_cnt))
712-
table.insert(msg, 'Cmd: ' .. cmdline)
713-
local full = table.concat(msg, '\n')
714-
if code == 0 then notify.info(full) else notify.err(full) end
715-
if code == 0 then
716-
local root = vim.fn.getcwd()
717-
LAST_EXE[U.norm(root)] = exe
718-
end
715+
table.insert(msg, string.format('Time: %.2fs | Errors: %d, Warnings: %d', secs, err_cnt, warn_cnt))
716+
table.insert(msg, 'Cmd: ' .. cmdline)
717+
local full = table.concat(msg, '\n')
718+
if code == 0 then notify.info(full) else notify.err(full) end
719+
if code == 0 then
720+
local root = vim.fn.getcwd()
721+
LAST_EXE[U.norm(root)] = exe
722+
end
723+
end)
719724
if opts.on_exit then pcall(opts.on_exit, code, exe) end
720725
done(code)
721726
end,

lua/quick-c/util.lua

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,36 @@ function U.parse_diagnostics(lines)
144144
table.insert(items, it)
145145
goto continue
146146
end
147+
-- Fallback: linker-style without type token: file:line: <message>
148+
do
149+
local f3, ln3, msg3 = l:match '^(.+):(%d+):%s*(.+)$'
150+
if f3 and ln3 and msg3 then
151+
local text = msg3
152+
local lower = text:lower()
153+
local is_err = false
154+
if lower:find('undefined reference') or lower:find('multiple definition') or lower:find('unresolved external symbol') or lower:find('linker error') then
155+
is_err = true
156+
end
157+
local it = {
158+
filename = clean_path(f3),
159+
lnum = tonumber(ln3),
160+
col = 1,
161+
text = text,
162+
type = is_err and 'E' or 'W',
163+
}
164+
do
165+
local ltxt = (it.text or ''):lower()
166+
if it.type == 'E' then
167+
if not ltxt:match('^%[error%]') then it.text = '[error] ' .. (it.text or '') end
168+
else
169+
if not ltxt:match('^%[warning%]') then it.text = '[warning] ' .. (it.text or '') end
170+
end
171+
end
172+
if it.type == 'E' then has_error = true else has_warning = true end
173+
table.insert(items, it)
174+
goto continue
175+
end
176+
end
147177
::continue::
148178
end
149179
return items, has_error, has_warning

0 commit comments

Comments
 (0)