feat: regex matching

This commit is contained in:
iff 2023-08-01 19:35:22 +02:00
parent a5762bf121
commit 3e367a09dd
8 changed files with 85 additions and 93 deletions

View file

@ -6,4 +6,4 @@ edition = "2021"
[dependencies]
colored = "2.0"
rule_parser = { path = "rule_parser" }
regex = "1.0"
regex-lite = "0.1"

View file

@ -82,7 +82,7 @@ The placeholder is evaluated as following:
- `{{command[1]}}`: The first argument of the command (the command itself has index of 0)
- `{{command[2:5]}}`: The second to fifth arguments. If any of the side is not specified, them it defaults to the start (if it is left) or the end (if it is right).
- `{{typo[2](fix1, fix2)}}`: This will try to change the second argument to candidates in the parenthesis. The argument in parentheses must have at least 2 values. Single arguments are reserved for specific matches, for instance, `path` to search all commands found in the `$PATH` environment.
- `{{opt(fix1, -*)}}`: Optional patterns that are found in the command. Note that all patterns matching this placeholder will not take a place when indexing (for example, in `rm {{opt(-r)}} file` the index of the file is always 1 regardless where `-r` was placed by the user).
- `{{opt::<Regular Expression>}}`: Optional patterns that are found in the command with RegEx (see RegEx crate for syntax). Note that all patterns matching this placeholder will not take a place when indexing.
The suggestion can have additional conditions to check. To specify the conditions, add a `#[...]` at the first line (just like derive macros in Rust). Available conditions:

View file

@ -55,9 +55,9 @@ fn gen_string_hashmap(rules: Vec<Rule>) -> String {
.map(|x| x.to_lowercase())
.collect::<Vec<String>>();
string_hashmap.push_str(&format!(
"(vec![\"{}\"], vec![\"{}\"]),",
"(vec![\"{}\"], vec![r###\"{}\"###]),",
pattern.join("\", \""),
suggest.join("\", \"")
suggest.join("\"###, r###\"")
));
}
string_hashmap.push_str("]),");

View file

@ -8,5 +8,6 @@ pattern = [
]
suggest = [
'''
{{typo[0](path)}} {{command[1:]}}'''
{{typo[0](path)}} {{command[1:]}}
'''
]

View file

@ -6,62 +6,62 @@ pattern = [
]
suggest = [
"""
{{command[0]}} {{typo[1](\
add,\
am,\
archive,\
bisect,\
branch,\
bundle,\
checkout,\
cherry-pick,\
citool,\
clean,\
clone,\
commit,\
describe,\
diff,\
fetch,\
format-patch,\
gc,\
gitk,\
grep,\
gui,\
init,\
log,\
maintenance,\
merge,\
mv,\
notes,\
pull,\
push,\
range-diff,\
rebase,\
reset,\
restore,\
{{command[0]}} {{typo[1](
add,
am,
archive,
bisect,
branch,
bundle,
checkout,
cherry-pick,
citool,
clean,
clone,
commit,
describe,
diff,
fetch,
format-patch,
gc,
gitk,
grep,
gui,
init,
log,
maintenance,
merge,
mv,
notes,
pull,
push,
range-diff,
rebase,
reset,
restore,
revert
rm,\
scalar,\
shortlog,\
show,\
sparse-checkout,\
stash,\
status,\
submodule,\
switch,\
tag,\
worktree,\
config,\
fast-export,\
fast-import,\
filter-branch,\
mergetool,\
pack-refs,\
prune,\
reflog,\
remote,\
repack,\
replace,\
)}} \
{{command[2:]}}"""
rm,
scalar,
shortlog,
show,
sparse-checkout,
stash,
status,
submodule,
switch,
tag,
worktree,
config,
fast-export,
fast-import,
filter-branch,
mergetool,
pack-refs,
prune,
reflog,
remote,
repack,
replace,
)}} {{command[2:]}}
"""
]

View file

@ -30,8 +30,10 @@ pattern = [
suggest = [
'''
#[executable(sudo)]
sudo {{command}}''',
sudo {{command}}
''',
'''
#[executable(doas)]
doas {{command}}''',
doas {{command}}
''',
]

View file

@ -4,13 +4,15 @@ command = "rm"
pattern = [ "is a directory", ]
suggest = [
'''
{{command}} --recursive'''
{{command}} --recursive
'''
]
[[match_err]]
pattern = [ "no such file or directory", ]
suggest = [
'''
{{command[0}} {{opt(-*)}} {{typo[-1](file)}}'''
{{command[0}} {{opt::(?:\s)-[\w]+}} {{typo[-1](file)}}
'''
]

View file

@ -1,4 +1,5 @@
use std::collections::HashMap;
use regex_lite::Regex;
use rule_parser::parse_rules;
@ -118,8 +119,8 @@ fn eval_suggest(suggest: &str, last_command: &str) -> String {
suggest = suggest.replace("{{command}}", &last_command);
}
while suggest.contains("{{opt") {
let placeholder_start = "{{opt";
while suggest.contains("{{opt::") {
let placeholder_start = "{{opt::";
let placeholder_end = "}}";
let start_index = suggest.find(placeholder_start).unwrap();
@ -130,31 +131,18 @@ fn eval_suggest(suggest: &str, last_command: &str) -> String {
let placeholder = start_index..end_index;
let args = start_index + placeholder_start.len()..end_index - placeholder_end.len();
let range = suggest[args.to_owned()].trim_matches(|c| c == '(' || c == ')');
let candidates = range.split(',').collect::<Vec<&str>>();
let mut opts = Vec::new();
let mut split_command = split_command(&last_command);
for opt in candidates {
let opt = opt.trim();
if opt.ends_with('*') {
let opt = opt.trim_end_matches('*');
for split in split_command.iter_mut() {
if split.starts_with(opt) {
opts.push(split.to_owned());
split.clear();
}
}
continue;
}
for split in split_command.iter_mut() {
if split == opt {
opts.push(split.to_owned());
split.clear();
}
}
}
last_command = split_command.join(" ");
let opt = &suggest[args.to_owned()];
let regex = opt.trim();
let regex = Regex::new(regex).unwrap();
let opts = regex
.find_iter(&last_command)
.map(|cap| cap.as_str().to_owned())
.collect::<Vec<String>>();
suggest.replace_range(placeholder, &opts.join(" "));
for opt in opts {
last_command = last_command.replace(&opt, "");
}
}
let split_command = split_command(&last_command);
@ -243,7 +231,6 @@ fn eval_suggest(suggest: &str, last_command: &str) -> String {
}
pub fn split_command(command: &str) -> Vec<String> {
use regex::Regex;
let regex = r#"([^\s"\\]+|"(?:\\.|[^"\\])*"|\\.)+"#;
let regex = Regex::new(regex).unwrap();
let split_command = regex