Skip to content

Commit 1df446e

Browse files
committed
Fix flyline tab completions
1 parent 7ef56ae commit 1df446e

3 files changed

Lines changed: 114 additions & 18 deletions

File tree

src/app/tab_completion.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,27 @@ fn run_flyline_compspec(
137137
let processed: Vec<ProcessedSuggestion> = candidates
138138
.into_iter()
139139
.filter_map(|c| {
140-
let value = c.get_value().to_string_lossy().to_string();
141-
let value = if let Some(qt) = quote_type {
142-
bash_funcs::quoting_function_rust(&value, qt, true, false)
143-
} else {
144-
value.clone()
145-
};
140+
let raw_value = c.get_value().to_string_lossy().to_string();
141+
let (mut prefix, value) =
142+
if let Some(delim_pos) = raw_value.find("PREFIX_DELIM") {
143+
let p = raw_value[..delim_pos].to_string();
144+
let v = raw_value[delim_pos + "PREFIX_DELIM".len()..].to_string();
145+
(p, v)
146+
} else {
147+
(String::new(), raw_value)
148+
};
149+
if !word_under_cursor.starts_with(&prefix) {
150+
prefix = String::new();
151+
}
146152
let (value, suffix) = if let Some(stripped) = value.strip_suffix("NO_SUFFIX") {
147153
(stripped.to_string(), "")
148154
} else {
149155
(value, " ")
150156
};
151-
let (prefix, value) = if let Some(delim_pos) = value.find("PREFIX_DELIM") {
152-
let p = value[..delim_pos].to_string();
153-
let v = value[delim_pos + "PREFIX_DELIM".len()..].to_string();
154-
(p, v)
157+
let value = if let Some(qt) = quote_type {
158+
bash_funcs::quoting_function_rust(&value, qt, true, false)
155159
} else {
156-
(String::new(), value)
160+
value
157161
};
158162

159163
let description = match c.get_help() {

src/app/ui.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,27 @@ mod tests {
21302130
assert_eq!(anchor, 0);
21312131
}
21322132

2133+
#[test]
2134+
fn test_auto_suggestions_popup_anchor_col_with_prefix() {
2135+
let anchor = auto_suggestions_popup_anchor_col(
2136+
10,
2137+
&SubString::from_parts("src/l", 5),
2138+
4,
2139+
"echo src/l",
2140+
10,
2141+
);
2142+
assert_eq!(anchor, 8);
2143+
2144+
let anchor = auto_suggestions_popup_anchor_col(
2145+
12,
2146+
&SubString::from_parts("", 19),
2147+
0,
2148+
"bind Ctrl+n always=",
2149+
19,
2150+
);
2151+
assert_eq!(anchor, 11);
2152+
}
2153+
21332154
#[test]
21342155
fn test_render_history_entry_wrapping_and_ellipsis() {
21352156
let palette = Palette::default();

src/cli.rs

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub fn complete_flyline_args(
142142
})
143143
.collect::<Vec<_>>();
144144

145-
let args_os_string = relevant_tokens
145+
let mut args_os_string: Vec<std::ffi::OsString> = relevant_tokens
146146
.iter()
147147
.map(|t| {
148148
if t.kind.is_whitespace() {
@@ -151,10 +151,27 @@ pub fn complete_flyline_args(
151151
std::ffi::OsString::from(&t.value)
152152
}
153153
})
154-
.chain(std::iter::once(std::ffi::OsString::from(
155-
bash_funcs::dequoting_function_rust(wuc),
156-
)))
157-
.collect::<Vec<_>>();
154+
.collect();
155+
156+
let dequoted_wuc = bash_funcs::dequoting_function_rust(wuc);
157+
let mut opt_prefix_to_strip = None;
158+
159+
let merged = if let Some(last_arg) = args_os_string.last_mut() {
160+
let last_str = last_arg.to_string_lossy();
161+
if last_str.ends_with('=') || last_str.ends_with(':') {
162+
opt_prefix_to_strip = Some(last_str.into_owned());
163+
last_arg.push(&dequoted_wuc);
164+
true
165+
} else {
166+
false
167+
}
168+
} else {
169+
false
170+
};
171+
172+
if !merged {
173+
args_os_string.push(std::ffi::OsString::from(dequoted_wuc));
174+
}
158175

159176
let index = args_os_string.len() - 1;
160177

@@ -174,8 +191,33 @@ pub fn complete_flyline_args(
174191
current_dir_asdf.as_deref(),
175192
) {
176193
Ok(candidates) => {
177-
log::info!("{:#?}", candidates.iter().take(10).collect::<Vec<_>>());
178-
Ok(candidates)
194+
let processed_candidates = if let Some(prefix) = opt_prefix_to_strip {
195+
candidates
196+
.into_iter()
197+
.map(|c| {
198+
let val = c.get_value().to_string_lossy();
199+
if val.contains("PREFIX_DELIM") {
200+
c
201+
} else if let Some(suffix) = val.strip_prefix(&prefix) {
202+
let new_val = format!("{}PREFIX_DELIM{}", prefix, suffix);
203+
let mut new_c = clap_complete::CompletionCandidate::new(new_val);
204+
if let Some(help) = c.get_help() {
205+
new_c = new_c.help(Some(help.clone()));
206+
}
207+
new_c
208+
} else {
209+
c
210+
}
211+
})
212+
.collect()
213+
} else {
214+
candidates
215+
};
216+
log::info!(
217+
"{:#?}",
218+
processed_candidates.iter().take(10).collect::<Vec<_>>()
219+
);
220+
Ok(processed_candidates)
179221
}
180222
Err(e) => {
181223
log::error!("Error generating bash completion: {e}");
@@ -1560,4 +1602,33 @@ mod tests {
15601602
assert!(values.contains(&"stop".to_string()));
15611603
assert!(values.contains(&"dump".to_string()));
15621604
}
1605+
1606+
#[test]
1607+
fn test_flyline_key_bind_completion() {
1608+
let raw_cmd = "flyline key bind BackTab agentModeError=";
1609+
let wuc = "";
1610+
let cursor_byte = raw_cmd.len();
1611+
let comps = complete_flyline_args(raw_cmd, wuc, cursor_byte).unwrap();
1612+
let values: Vec<String> = comps
1613+
.into_iter()
1614+
.map(|c| c.get_value().to_string_lossy().into_owned())
1615+
.collect();
1616+
assert!(!values.is_empty());
1617+
}
1618+
1619+
#[test]
1620+
fn test_flyline_option_completion() {
1621+
let raw_cmd = "flyline --show-animations=t";
1622+
let wuc = "t";
1623+
let cursor_byte = raw_cmd.len();
1624+
let comps = complete_flyline_args(raw_cmd, wuc, cursor_byte).unwrap();
1625+
let values: Vec<String> = comps
1626+
.into_iter()
1627+
.map(|c| c.get_value().to_string_lossy().into_owned())
1628+
.collect();
1629+
assert!(
1630+
values.contains(&"--show-animations=PREFIX_DELIMtrue".to_string())
1631+
|| values.contains(&"--show-animations=PREFIX_DELIMfalse".to_string())
1632+
);
1633+
}
15631634
}

0 commit comments

Comments
 (0)