feat: make file mention completion tunable
This commit is contained in:
@@ -578,6 +578,16 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
|
||||
app.paste_burst.clear_after_explicit_paste();
|
||||
}
|
||||
}
|
||||
"mention_menu_limit" | "mention_limit" => {
|
||||
app.mention_menu_limit = settings.mention_menu_limit;
|
||||
app.composer.mention_completion_cache = None;
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
"mention_walk_depth" | "mention_depth" | "completions_walk_depth" => {
|
||||
app.mention_walk_depth = settings.mention_walk_depth;
|
||||
app.composer.mention_completion_cache = None;
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
"transcript_spacing" | "spacing" => {
|
||||
app.transcript_spacing =
|
||||
crate::tui::app::TranscriptSpacing::from_setting(&settings.transcript_spacing);
|
||||
|
||||
@@ -66,6 +66,10 @@ pub struct SettingsSection {
|
||||
pub composer_density: ComposerDensityValue,
|
||||
pub composer_border: bool,
|
||||
pub composer_vim_mode: ComposerVimModeValue,
|
||||
#[schemars(range(min = 0))]
|
||||
pub mention_menu_limit: usize,
|
||||
#[schemars(range(min = 0))]
|
||||
pub mention_walk_depth: usize,
|
||||
pub transcript_spacing: TranscriptSpacingValue,
|
||||
pub status_indicator: StatusIndicatorValue,
|
||||
pub synchronized_output: SynchronizedOutputValue,
|
||||
@@ -327,6 +331,8 @@ pub fn build_document(app: &App, config: &Config) -> Result<ConfigUiDocument> {
|
||||
composer_density: settings.composer_density.as_str().into(),
|
||||
composer_border: settings.composer_border,
|
||||
composer_vim_mode: settings.composer_vim_mode.as_str().into(),
|
||||
mention_menu_limit: settings.mention_menu_limit,
|
||||
mention_walk_depth: settings.mention_walk_depth,
|
||||
transcript_spacing: settings.transcript_spacing.as_str().into(),
|
||||
status_indicator: settings.status_indicator.as_str().into(),
|
||||
synchronized_output: settings.synchronized_output.as_str().into(),
|
||||
@@ -503,6 +509,14 @@ pub fn apply_document(
|
||||
"composer_vim_mode",
|
||||
doc.settings.composer_vim_mode.as_setting(),
|
||||
),
|
||||
(
|
||||
"mention_menu_limit",
|
||||
&doc.settings.mention_menu_limit.to_string(),
|
||||
),
|
||||
(
|
||||
"mention_walk_depth",
|
||||
&doc.settings.mention_walk_depth.to_string(),
|
||||
),
|
||||
(
|
||||
"transcript_spacing",
|
||||
doc.settings.transcript_spacing.as_setting(),
|
||||
|
||||
@@ -193,6 +193,13 @@ pub struct Settings {
|
||||
/// Enable rapid-key paste-burst detection for terminals that do not emit
|
||||
/// bracketed-paste events. Independent from `bracketed_paste`.
|
||||
pub paste_burst_detection: bool,
|
||||
/// Maximum number of file-mention popup candidates retained before the
|
||||
/// composer renders its visible window. The widget paginates by terminal
|
||||
/// height, so this is a data-side cap rather than a visible-row budget.
|
||||
pub mention_menu_limit: usize,
|
||||
/// Maximum workspace depth for `@`-mention completion walks. `0` means
|
||||
/// unlimited depth; use with care in very large repositories.
|
||||
pub mention_walk_depth: usize,
|
||||
/// Show thinking blocks from the model
|
||||
pub show_thinking: bool,
|
||||
/// Show detailed tool output
|
||||
@@ -297,6 +304,8 @@ impl Default for Settings {
|
||||
fancy_animations: true,
|
||||
bracketed_paste: true,
|
||||
paste_burst_detection: true,
|
||||
mention_menu_limit: 128,
|
||||
mention_walk_depth: 6,
|
||||
show_thinking: true,
|
||||
show_tool_details: true,
|
||||
locale: "auto".to_string(),
|
||||
@@ -503,6 +512,12 @@ impl Settings {
|
||||
"paste_burst_detection" | "paste_burst" => {
|
||||
self.paste_burst_detection = parse_bool(value)?;
|
||||
}
|
||||
"mention_menu_limit" | "mention_limit" => {
|
||||
self.mention_menu_limit = parse_usize_setting("mention_menu_limit", value)?;
|
||||
}
|
||||
"mention_walk_depth" | "mention_depth" | "completions_walk_depth" => {
|
||||
self.mention_walk_depth = parse_usize_setting("mention_walk_depth", value)?;
|
||||
}
|
||||
"show_thinking" | "thinking" => {
|
||||
self.show_thinking = parse_bool(value)?;
|
||||
}
|
||||
@@ -694,6 +709,8 @@ impl Settings {
|
||||
" paste_burst_detect: {}",
|
||||
self.paste_burst_detection
|
||||
));
|
||||
lines.push(format!(" mention_menu_limit: {}", self.mention_menu_limit));
|
||||
lines.push(format!(" mention_walk_depth: {}", self.mention_walk_depth));
|
||||
lines.push(format!(" show_thinking: {}", self.show_thinking));
|
||||
lines.push(format!(" show_tool_details: {}", self.show_tool_details));
|
||||
lines.push(format!(" locale: {}", self.locale));
|
||||
@@ -768,6 +785,14 @@ impl Settings {
|
||||
"paste_burst_detection",
|
||||
"Fallback rapid-key paste detection: on/off",
|
||||
),
|
||||
(
|
||||
"mention_menu_limit",
|
||||
"Maximum @-mention popup candidates retained before rendering (default 128)",
|
||||
),
|
||||
(
|
||||
"mention_walk_depth",
|
||||
"Maximum @-mention workspace walk depth; 0 means unlimited (default 6)",
|
||||
),
|
||||
("show_thinking", "Show model thinking: on/off"),
|
||||
("show_tool_details", "Show detailed tool output: on/off"),
|
||||
(
|
||||
@@ -899,6 +924,14 @@ fn parse_bool(value: &str) -> Result<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_usize_setting(key: &str, value: &str) -> Result<usize> {
|
||||
value.trim().parse::<usize>().map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to update setting: invalid {key} '{value}'. Expected 0 or a positive integer."
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn normalize_mode(value: &str) -> &str {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"edit" => "agent",
|
||||
@@ -1119,6 +1152,28 @@ mod tests {
|
||||
assert!(!settings.paste_burst_detection);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mention_completion_caps_are_configurable() {
|
||||
let mut settings = Settings::default();
|
||||
assert_eq!(settings.mention_menu_limit, 128);
|
||||
assert_eq!(settings.mention_walk_depth, 6);
|
||||
|
||||
settings
|
||||
.set("mention_menu_limit", "256")
|
||||
.expect("set mention menu limit");
|
||||
settings
|
||||
.set("mention_walk_depth", "0")
|
||||
.expect("allow unlimited walk depth");
|
||||
|
||||
assert_eq!(settings.mention_menu_limit, 256);
|
||||
assert_eq!(settings.mention_walk_depth, 0);
|
||||
|
||||
let err = settings
|
||||
.set("mention_walk_depth", "deep")
|
||||
.expect_err("non-numeric depth should fail");
|
||||
assert!(err.to_string().contains("invalid mention_walk_depth"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locale_normalizes_supported_values_and_rejects_unknowns() {
|
||||
let mut settings = Settings::default();
|
||||
|
||||
@@ -882,6 +882,9 @@ pub struct MentionCompletionCache {
|
||||
pub partial: String,
|
||||
/// Candidate limit used for this completion walk.
|
||||
pub limit: usize,
|
||||
/// Workspace depth limit used for this completion walk. Included so live
|
||||
/// config changes invalidate cached popup results.
|
||||
pub walk_depth: usize,
|
||||
/// Cached completion entries.
|
||||
pub entries: Vec<String>,
|
||||
}
|
||||
@@ -1198,6 +1201,12 @@ pub struct App {
|
||||
/// sequences (e.g. Windows CMD without `WT_SESSION`) get page-scrolling
|
||||
/// without any explicit config (#1443).
|
||||
pub composer_arrows_scroll: bool,
|
||||
/// Data-side cap for the `@`-mention popup. The renderer still limits the
|
||||
/// visible rows to available terminal height.
|
||||
pub mention_menu_limit: usize,
|
||||
/// Maximum workspace depth for `@`-mention completion walks. `0` means
|
||||
/// unlimited depth.
|
||||
pub mention_walk_depth: usize,
|
||||
pub use_bracketed_paste: bool,
|
||||
pub use_paste_burst_detection: bool,
|
||||
/// Set to `true` the first time a real `Event::Paste` arrives during a
|
||||
@@ -2087,6 +2096,8 @@ impl App {
|
||||
.as_ref()
|
||||
.and_then(|tui| tui.composer_arrows_scroll)
|
||||
.unwrap_or_else(|| default_composer_arrows_scroll(use_mouse_capture)),
|
||||
mention_menu_limit: settings.mention_menu_limit,
|
||||
mention_walk_depth: settings.mention_walk_depth,
|
||||
session_title: None,
|
||||
receipt_text: None,
|
||||
receipt_started_at: None,
|
||||
|
||||
@@ -166,7 +166,11 @@ pub fn find_file_mention_completions(
|
||||
/// captures the process CWD so the resolver and completion walker honor the
|
||||
/// user's launch directory when it differs from `--workspace`.
|
||||
fn workspace_for_app(app: &App) -> Workspace {
|
||||
Workspace::with_cwd(app.workspace.clone(), std::env::current_dir().ok())
|
||||
Workspace::with_cwd_and_depth(
|
||||
app.workspace.clone(),
|
||||
std::env::current_dir().ok(),
|
||||
app.mention_walk_depth,
|
||||
)
|
||||
}
|
||||
|
||||
/// Resolve the `@`-mention completion popup contents for the current
|
||||
@@ -197,16 +201,18 @@ pub fn visible_mention_menu_entries(app: &mut App, limit: usize) -> Vec<String>
|
||||
|
||||
let workspace = app.workspace.clone();
|
||||
let cwd = std::env::current_dir().ok();
|
||||
let walk_depth = app.mention_walk_depth;
|
||||
if let Some(ref cache) = app.composer.mention_completion_cache
|
||||
&& cache.workspace == workspace
|
||||
&& cache.cwd == cwd
|
||||
&& cache.partial == partial
|
||||
&& cache.limit == limit
|
||||
&& cache.walk_depth == walk_depth
|
||||
{
|
||||
return cache.entries.clone();
|
||||
}
|
||||
|
||||
let ws = Workspace::with_cwd(workspace.clone(), cwd.clone());
|
||||
let ws = Workspace::with_cwd_and_depth(workspace.clone(), cwd.clone(), walk_depth);
|
||||
let entries = find_file_mention_completions(&ws, &partial, limit);
|
||||
|
||||
app.composer.mention_completion_cache = Some(MentionCompletionCache {
|
||||
@@ -214,6 +220,7 @@ pub fn visible_mention_menu_entries(app: &mut App, limit: usize) -> Vec<String>
|
||||
cwd,
|
||||
partial,
|
||||
limit,
|
||||
walk_depth,
|
||||
entries: entries.clone(),
|
||||
});
|
||||
|
||||
|
||||
@@ -141,7 +141,6 @@ use super::widgets::{ChatWidget, ComposerWidget, HeaderData, HeaderWidget, Rende
|
||||
/// Bumped from 6 to 128 to fix #64 (selection couldn't reach commands beyond
|
||||
/// the visible window because the source list itself was capped).
|
||||
const SLASH_MENU_LIMIT: usize = 128;
|
||||
const MENTION_MENU_LIMIT: usize = 6;
|
||||
const MIN_CHAT_HEIGHT: u16 = 3;
|
||||
const MIN_COMPOSER_HEIGHT: u16 = 2;
|
||||
const CONTEXT_WARNING_THRESHOLD_PERCENT: f64 = 85.0;
|
||||
@@ -3023,8 +3022,9 @@ async fn run_event_loop(
|
||||
if slash_menu_open && app.slash_menu_selected >= slash_menu_entries.len() {
|
||||
app.slash_menu_selected = slash_menu_entries.len().saturating_sub(1);
|
||||
}
|
||||
let mention_menu_limit = app.mention_menu_limit;
|
||||
let mention_menu_entries =
|
||||
crate::tui::file_mention::visible_mention_menu_entries(app, MENTION_MENU_LIMIT);
|
||||
crate::tui::file_mention::visible_mention_menu_entries(app, mention_menu_limit);
|
||||
let mention_menu_open = !mention_menu_entries.is_empty();
|
||||
if mention_menu_open && app.mention_menu_selected >= mention_menu_entries.len() {
|
||||
app.mention_menu_selected = mention_menu_entries.len().saturating_sub(1);
|
||||
@@ -6073,8 +6073,9 @@ fn render(f: &mut Frame, app: &mut App) {
|
||||
let header_height = 1;
|
||||
let footer_height = 1;
|
||||
let slash_menu_entries = visible_slash_menu_entries(app, SLASH_MENU_LIMIT);
|
||||
let mention_menu_limit = app.mention_menu_limit;
|
||||
let mention_menu_entries =
|
||||
crate::tui::file_mention::visible_mention_menu_entries(app, MENTION_MENU_LIMIT);
|
||||
crate::tui::file_mention::visible_mention_menu_entries(app, mention_menu_limit);
|
||||
if !mention_menu_entries.is_empty() && app.mention_menu_selected >= mention_menu_entries.len() {
|
||||
app.mention_menu_selected = mention_menu_entries.len().saturating_sub(1);
|
||||
}
|
||||
|
||||
@@ -761,6 +761,20 @@ impl ConfigView {
|
||||
editable: true,
|
||||
scope: ConfigScope::Saved,
|
||||
},
|
||||
ConfigRow {
|
||||
section: ConfigSection::Composer,
|
||||
key: "mention_menu_limit".to_string(),
|
||||
value: settings.mention_menu_limit.to_string(),
|
||||
editable: true,
|
||||
scope: ConfigScope::Saved,
|
||||
},
|
||||
ConfigRow {
|
||||
section: ConfigSection::Composer,
|
||||
key: "mention_walk_depth".to_string(),
|
||||
value: settings.mention_walk_depth.to_string(),
|
||||
editable: true,
|
||||
scope: ConfigScope::Saved,
|
||||
},
|
||||
ConfigRow {
|
||||
section: ConfigSection::Sidebar,
|
||||
key: "sidebar_width".to_string(),
|
||||
|
||||
@@ -26,12 +26,13 @@ use std::sync::OnceLock;
|
||||
/// during a session, build a fresh `Workspace`. Fuzzy lookups are backed by a
|
||||
/// lazy basename → paths index built once on first miss and reused for the
|
||||
/// rest of the session — without it, every mis-typed mention triggered a full
|
||||
/// `WalkBuilder` traversal up to depth 6 (Gemini code-review feedback).
|
||||
/// `WalkBuilder` traversal up to the configured completion depth.
|
||||
#[derive(Debug)]
|
||||
pub struct Workspace {
|
||||
pub root: PathBuf,
|
||||
cwd: Option<PathBuf>,
|
||||
file_index: OnceLock<HashMap<String, Vec<PathBuf>>>,
|
||||
completion_walk_depth: Option<usize>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
@@ -48,10 +49,17 @@ impl Workspace {
|
||||
/// resolution against a known directory without depending on (and
|
||||
/// mutating) the process's real working directory.
|
||||
pub fn with_cwd(root: PathBuf, cwd: Option<PathBuf>) -> Self {
|
||||
Self::with_cwd_and_depth(root, cwd, DEFAULT_COMPLETIONS_WALK_DEPTH)
|
||||
}
|
||||
|
||||
/// Construct with an explicit completion walk depth. A depth of `0`
|
||||
/// disables the depth limit for users with deeply nested workspaces.
|
||||
pub fn with_cwd_and_depth(root: PathBuf, cwd: Option<PathBuf>, walk_depth: usize) -> Self {
|
||||
Self {
|
||||
root,
|
||||
cwd,
|
||||
file_index: OnceLock::new(),
|
||||
completion_walk_depth: normalize_completion_walk_depth(walk_depth),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +105,7 @@ impl Workspace {
|
||||
fn build_file_index(&self) -> HashMap<String, Vec<PathBuf>> {
|
||||
let mut index: HashMap<String, Vec<PathBuf>> = HashMap::new();
|
||||
let mut total: usize = 0;
|
||||
let builder = discovery_walk_builder(&self.root, Some(6));
|
||||
let builder = discovery_walk_builder(&self.root, self.completion_walk_depth);
|
||||
|
||||
for entry in builder.build().flatten() {
|
||||
if total >= FILE_INDEX_MAX_ENTRIES {
|
||||
@@ -135,8 +143,10 @@ impl Workspace {
|
||||
.hidden(true)
|
||||
.follow_links(false)
|
||||
.git_ignore(false)
|
||||
.ignore(false)
|
||||
.max_depth(Some(5));
|
||||
.ignore(false);
|
||||
if let Some(depth) = child_completion_walk_depth(self.completion_walk_depth) {
|
||||
dot_builder.max_depth(Some(depth));
|
||||
}
|
||||
for entry in dot_builder.build().flatten() {
|
||||
if total >= FILE_INDEX_MAX_ENTRIES {
|
||||
break;
|
||||
@@ -163,7 +173,11 @@ impl Workspace {
|
||||
// hidden/ignored path the user might `@`-mention (e.g. a project's
|
||||
// own `.generated/specs/`). `local_reference_paths` walks with
|
||||
// gitignore disabled but still honors `.deepseekignore`.
|
||||
for path in local_reference_paths(&self.root, LOCAL_REFERENCE_SCAN_LIMIT) {
|
||||
for path in local_reference_paths(
|
||||
&self.root,
|
||||
LOCAL_REFERENCE_SCAN_LIMIT,
|
||||
self.completion_walk_depth,
|
||||
) {
|
||||
if total >= FILE_INDEX_MAX_ENTRIES {
|
||||
break;
|
||||
}
|
||||
@@ -220,6 +234,7 @@ impl Workspace {
|
||||
&mut prefix_hits,
|
||||
&mut substring_hits,
|
||||
&mut seen,
|
||||
self.completion_walk_depth,
|
||||
);
|
||||
add_local_reference_completions(
|
||||
cwd,
|
||||
@@ -229,6 +244,7 @@ impl Workspace {
|
||||
&mut prefix_hits,
|
||||
&mut substring_hits,
|
||||
&mut seen,
|
||||
self.completion_walk_depth,
|
||||
);
|
||||
}
|
||||
walk_for_completions(
|
||||
@@ -239,6 +255,7 @@ impl Workspace {
|
||||
&mut prefix_hits,
|
||||
&mut substring_hits,
|
||||
&mut seen,
|
||||
self.completion_walk_depth,
|
||||
);
|
||||
add_local_reference_completions(
|
||||
&self.root,
|
||||
@@ -248,6 +265,7 @@ impl Workspace {
|
||||
&mut prefix_hits,
|
||||
&mut substring_hits,
|
||||
&mut seen,
|
||||
self.completion_walk_depth,
|
||||
);
|
||||
|
||||
prefix_hits.sort();
|
||||
@@ -258,10 +276,18 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
/// Maximum directory depth walked when surfacing file-mention completions.
|
||||
/// Default directory depth walked when surfacing file-mention completions.
|
||||
/// Mirrors the existing `project_tree` cutoff and keeps Tab snappy in deep
|
||||
/// monorepos.
|
||||
const COMPLETIONS_WALK_DEPTH: usize = 6;
|
||||
/// monorepos unless the user opts into a deeper walk.
|
||||
pub const DEFAULT_COMPLETIONS_WALK_DEPTH: usize = 6;
|
||||
|
||||
fn normalize_completion_walk_depth(depth: usize) -> Option<usize> {
|
||||
if depth == 0 { None } else { Some(depth) }
|
||||
}
|
||||
|
||||
fn child_completion_walk_depth(depth: Option<usize>) -> Option<usize> {
|
||||
depth.map(|depth| depth.saturating_sub(1))
|
||||
}
|
||||
|
||||
/// Hard cap on the number of `(file or directory)` entries indexed by
|
||||
/// [`Workspace::build_file_index`]. The fuzzy-resolve index is a
|
||||
@@ -360,8 +386,9 @@ fn walk_for_completions(
|
||||
prefix_hits: &mut Vec<String>,
|
||||
substring_hits: &mut Vec<String>,
|
||||
seen: &mut HashSet<PathBuf>,
|
||||
max_depth: Option<usize>,
|
||||
) {
|
||||
let builder = discovery_walk_builder(walk_root, Some(COMPLETIONS_WALK_DEPTH));
|
||||
let builder = discovery_walk_builder(walk_root, max_depth);
|
||||
|
||||
for entry in builder.build().flatten() {
|
||||
if prefix_hits.len() + substring_hits.len() >= limit {
|
||||
@@ -405,7 +432,7 @@ fn walk_for_completions(
|
||||
prefix_hits,
|
||||
substring_hits,
|
||||
seen,
|
||||
Some(COMPLETIONS_WALK_DEPTH),
|
||||
max_depth,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -420,12 +447,13 @@ fn add_local_reference_completions(
|
||||
prefix_hits: &mut Vec<String>,
|
||||
substring_hits: &mut Vec<String>,
|
||||
seen: &mut HashSet<PathBuf>,
|
||||
max_depth: Option<usize>,
|
||||
) {
|
||||
if !should_try_local_reference_completion(needle) {
|
||||
return;
|
||||
}
|
||||
|
||||
for path in local_reference_paths(root, LOCAL_REFERENCE_SCAN_LIMIT) {
|
||||
for path in local_reference_paths(root, LOCAL_REFERENCE_SCAN_LIMIT, max_depth) {
|
||||
if prefix_hits.len() + substring_hits.len() >= limit {
|
||||
break;
|
||||
}
|
||||
@@ -460,16 +488,18 @@ fn should_try_local_reference_completion(needle: &str) -> bool {
|
||||
needle.starts_with('.') || needle.contains('/') || needle.contains('\\')
|
||||
}
|
||||
|
||||
fn local_reference_paths(root: &Path, limit: usize) -> Vec<PathBuf> {
|
||||
fn local_reference_paths(root: &Path, limit: usize, max_depth: Option<usize>) -> Vec<PathBuf> {
|
||||
let mut out = Vec::new();
|
||||
let mut builder = WalkBuilder::new(root);
|
||||
builder
|
||||
.hidden(false)
|
||||
.follow_links(false)
|
||||
.max_depth(Some(COMPLETIONS_WALK_DEPTH))
|
||||
.git_ignore(false)
|
||||
.git_global(false)
|
||||
.git_exclude(false);
|
||||
if let Some(depth) = max_depth {
|
||||
builder.max_depth(Some(depth));
|
||||
}
|
||||
let _ = builder.add_custom_ignore_filename(".deepseekignore");
|
||||
let root_for_filter = root.to_path_buf();
|
||||
builder.filter_entry(move |entry| {
|
||||
@@ -502,6 +532,7 @@ impl Clone for Workspace {
|
||||
root: self.root.clone(),
|
||||
cwd: self.cwd.clone(),
|
||||
file_index: OnceLock::new(),
|
||||
completion_walk_depth: self.completion_walk_depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1442,6 +1473,41 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_completions_honor_configured_walk_depth() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let deep_dir = tmp.path().join("a/b/c/d/e/f/g/h");
|
||||
std::fs::create_dir_all(&deep_dir).unwrap();
|
||||
std::fs::write(deep_dir.join("target.txt"), "target").unwrap();
|
||||
|
||||
let default_ws = Workspace::with_cwd(tmp.path().to_path_buf(), None);
|
||||
let default_entries = default_ws.completions("target", 16);
|
||||
assert!(
|
||||
!default_entries
|
||||
.iter()
|
||||
.any(|entry| entry.ends_with("target.txt")),
|
||||
"default depth should keep very deep entries out of the hot completion path: {default_entries:?}",
|
||||
);
|
||||
|
||||
let deep_ws = Workspace::with_cwd_and_depth(tmp.path().to_path_buf(), None, 16);
|
||||
let deep_entries = deep_ws.completions("target", 16);
|
||||
assert!(
|
||||
deep_entries
|
||||
.iter()
|
||||
.any(|entry| entry.ends_with("target.txt")),
|
||||
"configured deeper walk should surface the nested file: {deep_entries:?}",
|
||||
);
|
||||
|
||||
let unlimited_ws = Workspace::with_cwd_and_depth(tmp.path().to_path_buf(), None, 0);
|
||||
let unlimited_entries = unlimited_ws.completions("target", 16);
|
||||
assert!(
|
||||
unlimited_entries
|
||||
.iter()
|
||||
.any(|entry| entry.ends_with("target.txt")),
|
||||
"depth 0 should disable the completion walk depth limit: {unlimited_entries:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_completions_surface_explicit_hidden_and_ignored_paths() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
|
||||
@@ -462,6 +462,12 @@ Common settings keys:
|
||||
- `paste_burst_detection` (on/off, default on): fallback rapid-key paste
|
||||
detection for terminals that do not emit bracketed-paste events. This is
|
||||
independent of terminal bracketed-paste mode.
|
||||
- `mention_menu_limit` (integer, default `128`): maximum number of
|
||||
`@`-mention popup candidates retained before the composer renders the
|
||||
visible window. The visible rows still depend on terminal height.
|
||||
- `mention_walk_depth` (integer, default `6`): maximum workspace depth for
|
||||
`@`-mention completion walks. Set to `0` for unlimited depth in deeply
|
||||
nested workspaces; keep the default in very large repos unless needed.
|
||||
- `show_thinking` (on/off)
|
||||
- `show_tool_details` (on/off)
|
||||
- `locale` (`auto`, `en`, `ja`, `zh-Hans`, `pt-BR`; default `auto`): UI chrome
|
||||
|
||||
Reference in New Issue
Block a user