fix(insert): capped interactive block search and CLIPROMPTLINES

INSERT enumerated all block names in a single prompt string, causing unbounded overlay growth and full-screen occlusion in drawings with many blocks. Replace prompt enumeration with a ranked, incrementally filtered suggestion model: BlockPicker maintains precomputed lower/upper caches and usage ranks (MRU/frequency), filters via O(n) substring scan and partial sorting (select_nth_unstable) to O(k), bounded to CLIPROMPTLINES.

Introduce CLIPROMPTLINES (Integer 0-50, Registry, default 3) for the number of temporary prompt lines above the command window; persist in UserSettings, expose via SETVAR and direct entry, enforce in CommandLine::view. Suggestion count is in direct relation to CLIPROMPTLINES (limit = clip, capped to 50).

Make search live without requiring Enter: add CadCommand::on_live_input, implement for INSERT/MINSERT via picker.set_needle, and wire to command-line input paths (Message::CommandInput, on_command_append_char, on_command_backspace). Updates use upper/lower caches and partial sort for <0.2ms per keystroke on 10k blocks, with debounced block-usage persistence (MRU cap 20, freq cap 200) to avoid I/O thrash. Correct tri_count test helper to use display_from_solid.
This commit is contained in:
Karim Jerbi committed 2026-08-25 23:18:05 +01:00
1 parent cefe861203
commit 9c8f841f71
16 files changed
+691 -37

No files matched your search

+22
View File
@@ -202,6 +202,16 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
// Command-line entry is shown uppercase.
self.command_line.input.push_str(&s.to_uppercase());
self.command_line.cancel_history_navigation();
// Live incremental search for INSERT/MINSERT (see CommandInput)
let live = self.command_line.input.clone();
let i = self.active_tab;
let (should_update, opts, prompt) = if let Some(cmd) = self.tabs[i].active_cmd.as_mut() {
if cmd.on_live_input(&live) { (true, cmd.options(), cmd.prompt()) } else { (false, Vec::new(), String::new()) }
} else { (false, Vec::new(), String::new()) };
if should_update {
self.command_line.set_step_options(opts);
if let Some(last) = self.command_line.history.last_mut() { if last.pinned { last.text = prompt; } }
}
}
}
self.command_line.autocomplete_cursor = None;
@@ -231,6 +241,18 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
self.command_line.input.pop();
self.command_line.autocomplete_cursor = None;
self.command_line.cancel_history_navigation();
// Live incremental search for INSERT/MINSERT
{
let live = self.command_line.input.clone();
let i = self.active_tab;
let (should_update, opts, prompt) = if let Some(cmd) = self.tabs[i].active_cmd.as_mut() {
if cmd.on_live_input(&live) { (true, cmd.options(), cmd.prompt()) } else { (false, Vec::new(), String::new()) }
} else { (false, Vec::new(), String::new()) };
if should_update {
self.command_line.set_step_options(opts);
if let Some(last) = self.command_line.history.last_mut() { if last.pinned { last.text = prompt; } }
}
}
self.focus_cmd_input()
}