Skip to content

Commit a91ce7e

Browse files
authored
Merge pull request #24 from AuroBreeze/dev
Dev
2 parents 2a12e64 + ad61103 commit a91ce7e

File tree

4 files changed

+136
-43
lines changed

4 files changed

+136
-43
lines changed

Release.md

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

3+
## v1.5.9 (2025-11-01)
4+
5+
### 改进
6+
- 终端聚焦体验:
7+
- cqb/cqR/cqr 执行后默认聚焦到终端(betterTerm 或内置)。
8+
- cqM 发送前不聚焦、发送后延迟聚焦,兼顾防按键泄漏与聚焦需求。
9+
- 终端选择器仅在需要时弹出:`choose_terminal = 'auto'` 模式下,0/1 个终端时直接使用默认策略,2+ 个终端时才打开选择器;`'always'` 始终弹出,`'never'` 从不弹出。
10+
11+
### 修复
12+
- 修复在多个终端时,cqM 通过 Telescope 选择终端会自动输入一个 “A” 的问题:
13+
- 打开选择器改为 `vim.defer_fn(..., 120)` 延迟触发,确保按键序列结束后再创建 UI。
14+
- 选择器内吞掉一次可能泄漏的 `A` 键(normal/insert 模式)。
15+
- 选择器使用 `initial_mode = 'normal'`,并在发送命令前不切换焦点,避免键继续泄漏。
16+
- 发送完成后再延迟聚焦至所选终端(betterTerm/内置)。
17+
- 稳定性:cqM 触发路径在键位侧包裹 `vim.schedule`,进一步降低残余按键注入概率。
18+
19+
### Bug 记录
20+
- 标题:cqM 在多终端(2 个及以上)时,Telescope 选择终端会自动输入一个 “A”。
21+
- 影响范围:有 Telescope 且存在 2+ 个内置终端的场景(Windows/macOS/Linux 均可能发生)。
22+
- 触发条件:通过 `<leader>cqM` 打开选择器,按键序列末尾的大写键在弹窗创建瞬间泄漏。
23+
- 根因分析:
24+
- 选择器在按键处理尚未完全结束时创建,存在残余按键注入窗口;
25+
- 发送前聚焦终端或选择器处于可编辑状态会放大该问题表现。
26+
- 修复方案:
27+
- 选择器延迟创建(`vim.defer_fn(..., 120)`)+ 选择器内吞掉一次 `A`(normal/insert);
28+
- 发送前不聚焦终端,发送成功后再延迟聚焦;
29+
- `auto` 模式下仅在 2+ 终端时弹选择器,0/1 终端直达默认策略。
30+
- 验证步骤:
31+
1) 打开 2 个以上内置终端;
32+
2) 执行 `<leader>cqM` 并在选择器中选择终端;
33+
3) 选择器不出现首个 “A”,命令正确发送;
34+
4) 发送后终端获得焦点(betterTerm/内置)。
35+
36+
### 兼容性
37+
- 无破坏性变更;默认配置即可获得上述改进。
38+
- betterTerm 用户:保留默认 `focus_on_run = true` 可配合新逻辑正常聚焦。
39+
40+
### 迁移指南
41+
- 无需迁移。若仍在极端环境下偶发按键残留,可将延迟从 120ms 调整为更大(后续将视反馈加入可配置项)。
42+
343
## v1.5.8 (2025-10-30)
444

545
### 新增

lua/quick-c/config.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ C.defaults = {
6666
focus_on_run = true,
6767
open_if_closed = true,
6868
},
69-
debug = {
69+
debug = { -- 搜索可执行文件的配置
7070
search = {
7171
up = 2,
7272
down = 2,
7373
ignore_dirs = { '.git', 'node_modules', '.cache' },
74-
dirs = nil,
74+
dirs = nil, -- 指定搜索目录,为空时使用当前文件所在目录
7575
},
76-
concurrency = 8,
76+
concurrency = 8, -- 并发工作者数
7777
},
7878
make = {
7979
enabled = true,

lua/quick-c/keys.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@ function K.setup(config, callbacks)
7474
map(km.debug, callbacks.debug, 'Quick-c: Debug current C/C++ exe')
7575
end
7676
if not disabled(km.make) then
77-
map(km.make, callbacks.make, 'Quick-c: Make targets (Telescope)')
77+
local function scheduled_make()
78+
vim.schedule(function()
79+
callbacks.make()
80+
end)
81+
end
82+
map(km.make, scheduled_make, 'Quick-c: Make targets (Telescope)')
7883
end
7984
if not disabled(km.cmake) and callbacks.cmake then
8085
map(km.cmake, callbacks.cmake, 'Quick-c: CMake targets (Telescope)')

lua/quick-c/terminal.lua

Lines changed: 87 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
local T = {}
22

3-
function T.run_in_native_terminal(config, is_windows, cmd)
3+
function T.run_in_native_terminal(config, is_windows, cmd, opts)
4+
opts = opts or {}
5+
local focus = (opts.focus ~= false)
6+
local prev = vim.api.nvim_get_current_win()
47
if config.terminal.open then
58
vim.cmd 'botright split | terminal'
69
vim.cmd(string.format('resize %d', config.terminal.height or 12))
@@ -11,13 +14,16 @@ function T.run_in_native_terminal(config, is_windows, cmd)
1114
if not chan then
1215
return false
1316
end
17+
if not focus then
18+
pcall(vim.api.nvim_set_current_win, prev)
19+
end
1420
vim.defer_fn(function()
1521
vim.fn.chansend(chan, cmd .. (is_windows() and '\r' or '\n'))
1622
end, 100)
1723
return true
1824
end
1925

20-
function T.run_in_betterterm(config, is_windows, cmd, notify_warn, notify_err)
26+
function T.run_in_betterterm(config, is_windows, cmd, notify_warn, notify_err, opts)
2127
local ok, betterTerm = pcall(require, 'betterTerm')
2228
if not ok or config.betterterm.enabled == false then
2329
return false
@@ -27,14 +33,20 @@ function T.run_in_betterterm(config, is_windows, cmd, notify_warn, notify_err)
2733
local delay = cfg.send_delay or 200
2834
local focus = (cfg.focus_on_run ~= false)
2935
local open_first = (cfg.open_if_closed ~= false)
36+
opts = opts or {}
37+
local want_focus = (opts.focus ~= false)
38+
local prev = vim.api.nvim_get_current_win()
3039
if open_first or focus then
3140
pcall(betterTerm.open, idx)
3241
end
42+
if not want_focus then
43+
pcall(vim.api.nvim_set_current_win, prev)
44+
end
3345
vim.defer_fn(function()
3446
local ok_send, err = pcall(betterTerm.send, cmd .. (is_windows() and '\r' or '\n'), idx)
3547
if not ok_send then
3648
notify_warn('Failed to send to betterTerm, using native terminal: ' .. tostring(err))
37-
if not T.run_in_native_terminal(config, is_windows, cmd) then
49+
if not T.run_in_native_terminal(config, is_windows, cmd, { focus = want_focus }) then
3850
notify_err 'Failed to open native terminal'
3951
end
4052
return
@@ -44,8 +56,8 @@ function T.run_in_betterterm(config, is_windows, cmd, notify_warn, notify_err)
4456
end
4557

4658
function T.run_make_in_terminal(config, is_windows, cmdline, notify_warn, notify_err)
47-
if not T.run_in_betterterm(config, is_windows, cmdline, notify_warn, notify_err) then
48-
if not T.run_in_native_terminal(config, is_windows, cmdline) then
59+
if not T.run_in_betterterm(config, is_windows, cmdline, notify_warn, notify_err, { focus = false }) then
60+
if not T.run_in_native_terminal(config, is_windows, cmdline, { focus = false }) then
4961
notify_err 'Unable to run make: cannot open terminal'
5062
end
5163
end
@@ -86,9 +98,6 @@ end
8698

8799
function T.send_to_builtin_terminal(is_windows, job, cmd, opts)
88100
opts = opts or {}
89-
if opts.bufnr and vim.api.nvim_buf_is_valid(opts.bufnr) then
90-
pcall(open_builtin_terminal_window, opts.config or {}, opts.bufnr)
91-
end
92101
local nl = is_windows() and '\r' or '\n'
93102
return pcall(vim.fn.chansend, job, cmd .. nl)
94103
end
@@ -98,7 +107,9 @@ function T.select_or_run_in_terminal(config, is_windows, cmdline, notify_warn, n
98107
local mode = ((config.make or {}).telescope or {}).choose_terminal or 'auto'
99108
local open_terms = T.list_open_builtin_terminals()
100109
local ok_t = pcall(require, 'telescope')
101-
if mode == 'never' or not ok_t or (mode == 'auto' and #open_terms == 0) then
110+
-- Only open picker when explicitly requested (always) or when there are 2+ terminals.
111+
-- In auto mode with 0 or 1 terminals, run default strategy directly to avoid key leakage.
112+
if mode == 'never' or not ok_t or (mode == 'auto' and #open_terms <= 1) then
102113
return T.run_make_in_terminal(config, is_windows, cmdline, notify_warn, notify_err)
103114
end
104115
local pickers = require 'telescope.pickers'
@@ -111,39 +122,76 @@ function T.select_or_run_in_terminal(config, is_windows, cmdline, notify_warn, n
111122
local disp = string.format('buf #%d | %s', it.bufnr, (it.name ~= '' and it.name or 'terminal'))
112123
table.insert(entries, { display = disp, kind = 'builtin', job = it.job, bufnr = it.bufnr })
113124
end
114-
pickers
115-
.new({}, {
116-
prompt_title = 'quick-c: select terminal to send',
117-
finder = finders.new_table {
118-
results = entries,
119-
entry_maker = function(e)
120-
return { value = e, display = e.display, ordinal = e.display }
121-
end,
122-
},
123-
sorter = conf.generic_sorter {},
124-
attach_mappings = function(_, map)
125-
local actions = require 'telescope.actions'
126-
local action_state = require 'telescope.actions.state'
127-
local function choose(bufnr)
128-
local entry = action_state.get_selected_entry()
129-
actions.close(bufnr)
130-
local v = entry.value
131-
if v.kind == 'default' then
132-
T.run_make_in_terminal(config, is_windows, cmdline, notify_warn, notify_err)
133-
else
134-
local ok = T.send_to_builtin_terminal(is_windows, v.job, cmdline, { bufnr = v.bufnr, config = config })
135-
if not ok then
136-
notify_warn 'Failed to send to selected terminal, using default strategy'
125+
vim.defer_fn(function()
126+
pickers
127+
.new({}, {
128+
prompt_title = 'quick-c: select terminal to send',
129+
initial_mode = 'normal',
130+
finder = finders.new_table {
131+
results = entries,
132+
entry_maker = function(e)
133+
return { value = e, display = e.display, ordinal = e.display }
134+
end,
135+
},
136+
sorter = conf.generic_sorter {},
137+
attach_mappings = function(_, map)
138+
local actions = require 'telescope.actions'
139+
local action_state = require 'telescope.actions.state'
140+
local function swallow_A()
141+
return true
142+
end
143+
local function choose(bufnr)
144+
local entry = action_state.get_selected_entry()
145+
actions.close(bufnr)
146+
local v = entry.value
147+
if v.kind == 'default' then
137148
T.run_make_in_terminal(config, is_windows, cmdline, notify_warn, notify_err)
149+
do
150+
local ok_bt, betterTerm = pcall(require, 'betterTerm')
151+
if ok_bt and (config.betterterm and config.betterterm.enabled ~= false) then
152+
local idx = (config.betterterm and config.betterterm.index) or 0
153+
local focus_on_run = (config.betterterm and config.betterterm.focus_on_run) ~= false
154+
if focus_on_run then
155+
vim.defer_fn(function()
156+
pcall(betterTerm.open, idx)
157+
end, 120)
158+
end
159+
end
160+
end
161+
else
162+
local ok = T.send_to_builtin_terminal(is_windows, v.job, cmdline, { bufnr = v.bufnr, config = config })
163+
if not ok then
164+
notify_warn 'Failed to send to selected terminal, using default strategy'
165+
T.run_make_in_terminal(config, is_windows, cmdline, notify_warn, notify_err)
166+
do
167+
local ok_bt, betterTerm = pcall(require, 'betterTerm')
168+
if ok_bt and (config.betterterm and config.betterterm.enabled ~= false) then
169+
local idx = (config.betterterm and config.betterterm.index) or 0
170+
local focus_on_run = (config.betterterm and config.betterterm.focus_on_run) ~= false
171+
if focus_on_run then
172+
vim.defer_fn(function()
173+
pcall(betterTerm.open, idx)
174+
end, 120)
175+
end
176+
end
177+
end
178+
end
179+
if ok then
180+
vim.defer_fn(function()
181+
pcall(open_builtin_terminal_window, config, v.bufnr)
182+
end, 120)
183+
end
138184
end
139185
end
140-
end
141-
map('i', '<CR>', choose)
142-
map('n', '<CR>', choose)
143-
return true
144-
end,
145-
})
146-
:find()
186+
map('i', '<CR>', choose)
187+
map('n', '<CR>', choose)
188+
map('n', 'A', swallow_A)
189+
map('i', 'A', swallow_A)
190+
return true
191+
end,
192+
})
193+
:find()
194+
end, 120)
147195
end
148196

149197
return T

0 commit comments

Comments
 (0)