Skip to content

Commit ab7646d

Browse files
authored
Respect WUC identification under : and = (#776)
#772 showed that flyline wasn't handling WUC identification when = and : were present. I reviewed the bash manual, and it looks like flyline is already handling (and I'd say better than readline) the other COMP_WORD_BREAK chars. : and = were two that I needed to split wuc on. Fixes #772
1 parent 41986fa commit ab7646d

3 files changed

Lines changed: 132 additions & 1 deletion

File tree

src/app/tab_completion.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,5 +2071,30 @@ mod tab_completion_tests {
20712071
items.sort();
20722072
assert_eq!(items, vec!["foo1/", "foo2/", "foo3/"]);
20732073
}
2074+
2075+
#[test]
2076+
fn test_getsub_completions() {
2077+
cd_to_example_fs();
2078+
2079+
// 1. Completing the options
2080+
let actual = run_completion("getsub --");
2081+
let names: Vec<&str> = actual.iter().map(|s| s.s.as_str()).collect();
2082+
assert_eq!(names, vec![
2083+
"--alternative=",
2084+
"--fix-audio=",
2085+
"--subtitle-type=",
2086+
"--translate-from="
2087+
]);
2088+
2089+
// 2. Completing the value after `=` (empty input)
2090+
let actual = run_completion("getsub --subtitle-type=");
2091+
let names: Vec<&str> = actual.iter().map(|s| s.s.as_str()).collect();
2092+
assert_eq!(names, vec!["json", "srt", "tsv", "txt", "vtt"]);
2093+
2094+
// 3. Completing the value after `=` with a prefix `t`
2095+
let actual = run_completion("getsub --subtitle-type=t");
2096+
let names: Vec<&str> = actual.iter().map(|s| s.s.as_str()).collect();
2097+
assert_eq!(names, vec!["tsv", "txt"]);
2098+
}
20742099
}
20752100
}

src/bash_funcs.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,40 @@ pub fn run_programmable_completions(
10821082
.collect();
10831083
let flags = CompletionFlags::from_alt(word_under_cursor, &filtered);
10841084
Ok(ProgrammableCompleteReturn::new(filtered, flags, true))
1085+
} else if command_word == "getsub" {
1086+
let completions = if full_command.contains("--subtitle-type=") {
1087+
vec![
1088+
"json".to_string(),
1089+
"txt".to_string(),
1090+
"srt".to_string(),
1091+
"tsv".to_string(),
1092+
"vtt".to_string(),
1093+
]
1094+
} else if full_command.contains("--fix-audio=") {
1095+
vec![
1096+
"backgroundNoise".to_string(),
1097+
"echoNoise".to_string(),
1098+
"windNoise".to_string(),
1099+
"lowVolume".to_string(),
1100+
"minimal".to_string(),
1101+
]
1102+
} else {
1103+
vec![
1104+
"--alternative=".to_string(),
1105+
"--fix-audio=".to_string(),
1106+
"--subtitle-type=".to_string(),
1107+
"--translate-from=".to_string(),
1108+
]
1109+
};
1110+
let filtered: Vec<String> = completions
1111+
.into_iter()
1112+
.filter(|s| s.starts_with(word_under_cursor))
1113+
.collect();
1114+
let mut flags = CompletionFlags::from_alt(word_under_cursor, &filtered);
1115+
if filtered.iter().all(|s| s.ends_with('=')) {
1116+
flags.no_suffix_desired = true;
1117+
}
1118+
Ok(ProgrammableCompleteReturn::new(filtered, flags, true))
10851119
} else if command_word == "cat" {
10861120
// do a naive filessytem glob.
10871121
// bash sometimes does this if nothing is returned by the prog comp spec.

src/tab_completion_context.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ pub fn get_completion_context<'a>(
351351
}),
352352
};
353353

354-
let word_under_cursor_range = match opt_cursor_node {
354+
let mut word_under_cursor_range = match opt_cursor_node {
355355
Some((_, cursor_node))
356356
if cursor_node.token.kind.is_whitespace()
357357
|| cursor_node.token.kind == TokenKind::Newline =>
@@ -431,6 +431,35 @@ pub fn get_completion_context<'a>(
431431
}
432432
};
433433

434+
if !word_under_cursor_range.is_empty() {
435+
let start = word_under_cursor_range.start;
436+
let end = word_under_cursor_range.end;
437+
438+
let mut split_indices = Vec::new();
439+
split_indices.push(start);
440+
for (offset, c) in buffer[start..end].char_indices() {
441+
if c == '=' || c == ':' {
442+
split_indices.push(start + offset);
443+
}
444+
}
445+
split_indices.push(end);
446+
447+
for i in 0..split_indices.len() - 1 {
448+
let mut seg_start = if i == 0 {
449+
split_indices[0]
450+
} else {
451+
split_indices[i] + 1
452+
};
453+
let seg_end = split_indices[i + 1];
454+
seg_start = seg_start.min(seg_end);
455+
456+
if (seg_start..=seg_end).contains(&cursor_byte_pos) {
457+
word_under_cursor_range = seg_start..seg_end;
458+
break;
459+
}
460+
}
461+
}
462+
434463
if !word_under_cursor_range
435464
.to_inclusive()
436465
.contains(&cursor_byte_pos)
@@ -1819,4 +1848,47 @@ mod tests {
18191848
let ctx = run_inline("ll ~/.█");
18201849
assert_eq!(ctx.word_under_cursor.as_ref(), "~/.");
18211850
}
1851+
1852+
#[test]
1853+
fn test_wuc_splitting() {
1854+
// --fo[CURSOR]o=qwe
1855+
let ctx = run_inline("getsub --fo█o=qwe");
1856+
assert_eq!(ctx.word_under_cursor.as_ref(), "--foo");
1857+
1858+
// --foo[CURSOR]=qwe
1859+
let ctx = run_inline("getsub --foo█=qwe");
1860+
assert_eq!(ctx.word_under_cursor.as_ref(), "--foo");
1861+
1862+
// --foo=[CURSOR]
1863+
let ctx = run_inline("getsub --foo=█");
1864+
assert_eq!(ctx.word_under_cursor.as_ref(), "");
1865+
1866+
// --foo=q[CURSOR]
1867+
let ctx = run_inline("getsub --foo=q█");
1868+
assert_eq!(ctx.word_under_cursor.as_ref(), "q");
1869+
1870+
// /asd/qwe:/foo/ba[cursor]r:/abc/def
1871+
let ctx = run_inline("/asd/qwe:/foo/ba█r:/abc/def");
1872+
assert_eq!(ctx.word_under_cursor.as_ref(), "/foo/bar");
1873+
1874+
// [cursor]/asd/qwe:/foo/bar:/abc/def
1875+
let ctx = run_inline("█/asd/qwe:/foo/bar:/abc/def");
1876+
assert_eq!(ctx.word_under_cursor.as_ref(), "/asd/qwe");
1877+
1878+
// /asd/qwe:/foo/bar:/abc/def[cursor]
1879+
let ctx = run_inline("/asd/qwe:/foo/bar:/abc/def█");
1880+
assert_eq!(ctx.word_under_cursor.as_ref(), "/abc/def");
1881+
1882+
// /asd/qwe[cursor]:/foo/bar:/abc/def
1883+
let ctx = run_inline("/asd/qwe█:/foo/bar:/abc/def");
1884+
assert_eq!(ctx.word_under_cursor.as_ref(), "/asd/qwe");
1885+
1886+
// /asd/qwe:[cursor]/foo/bar:/abc/def
1887+
let ctx = run_inline("/asd/qwe:█/foo/bar:/abc/def");
1888+
assert_eq!(ctx.word_under_cursor.as_ref(), "/foo/bar");
1889+
1890+
// /asd/qwe::/foo/ba[cursor]r
1891+
let ctx = run_inline("/asd/qwe::/foo/ba█r");
1892+
assert_eq!(ctx.word_under_cursor.as_ref(), "/foo/bar");
1893+
}
18221894
}

0 commit comments

Comments
 (0)