mirror of
https://github.com/TECHNOFAB11/pay-respects.git
synced 2025-12-15 07:43:51 +01:00
feat: multi-suggest
This commit is contained in:
parent
e6ed9d617b
commit
1ddfbef7e7
8 changed files with 111 additions and 81 deletions
48
src/modes.rs
48
src/modes.rs
|
|
@ -1,6 +1,5 @@
|
|||
use crate::shell::Data;
|
||||
use crate::style::highlight_difference;
|
||||
use crate::suggestions::{best_match_path, suggest_command};
|
||||
use crate::suggestions::{best_match_path, suggest_candidates};
|
||||
use crate::system;
|
||||
use crate::{shell, suggestions};
|
||||
use colored::Colorize;
|
||||
|
|
@ -12,32 +11,22 @@ pub fn suggestion(data: &mut Data) {
|
|||
|
||||
loop {
|
||||
last_command = data.command.clone();
|
||||
let suggestion = {
|
||||
let command = suggest_command(data);
|
||||
if command.is_none() {
|
||||
break;
|
||||
};
|
||||
|
||||
let mut command = command.unwrap();
|
||||
shell::shell_syntax(&shell, &mut command);
|
||||
command
|
||||
};
|
||||
data.update_suggest(&suggestion);
|
||||
data.expand_suggest();
|
||||
|
||||
let highlighted_suggestion = {
|
||||
let difference = highlight_difference(&shell, &suggestion, &last_command);
|
||||
if difference.is_none() {
|
||||
break;
|
||||
};
|
||||
difference.unwrap()
|
||||
suggest_candidates(data);
|
||||
if data.candidates.is_empty() {
|
||||
break;
|
||||
};
|
||||
|
||||
let execution = suggestions::confirm_suggestion(data, &highlighted_suggestion);
|
||||
for candidate in &mut data.candidates {
|
||||
shell::shell_syntax(&shell, candidate);
|
||||
}
|
||||
|
||||
suggestions::select_candidate(data);
|
||||
|
||||
let execution = suggestions::confirm_suggestion(data);
|
||||
if execution.is_ok() {
|
||||
return;
|
||||
} else {
|
||||
data.update_command(&suggestion);
|
||||
data.update_command(&data.suggest.clone().unwrap());
|
||||
let msg = Some(
|
||||
execution
|
||||
.err()
|
||||
|
|
@ -63,7 +52,6 @@ pub fn suggestion(data: &mut Data) {
|
|||
|
||||
pub fn cnf(data: &mut Data) {
|
||||
let shell = data.shell.clone();
|
||||
let last_command = data.command.clone();
|
||||
let mut split_command = data.split.clone();
|
||||
|
||||
let executable = split_command[0].as_str();
|
||||
|
|
@ -73,12 +61,11 @@ pub fn cnf(data: &mut Data) {
|
|||
let best_match = best_match.unwrap();
|
||||
split_command[0] = best_match;
|
||||
let suggest = split_command.join(" ");
|
||||
data.update_suggest(&suggest);
|
||||
data.expand_suggest();
|
||||
|
||||
let highlighted_suggestion =
|
||||
highlight_difference(&shell, &suggest, &last_command).unwrap();
|
||||
let status = suggestions::confirm_suggestion(data, &highlighted_suggestion);
|
||||
data.candidates.push(suggest.clone());
|
||||
suggestions::select_candidate(data);
|
||||
|
||||
let status = suggestions::confirm_suggestion(data);
|
||||
if status.is_err() {
|
||||
data.update_command(&suggest);
|
||||
let msg = Some(
|
||||
|
|
@ -96,7 +83,6 @@ pub fn cnf(data: &mut Data) {
|
|||
let package_manager = match system::get_package_manager(&shell) {
|
||||
Some(package_manager) => package_manager,
|
||||
None => {
|
||||
eprintln!("no package manager found");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
@ -120,7 +106,7 @@ pub fn cnf(data: &mut Data) {
|
|||
|
||||
// retry after installing package
|
||||
if system::install_package(&shell, &package_manager, &package) {
|
||||
let _ = suggestions::confirm_suggestion(data, &last_command);
|
||||
let _ = suggestions::confirm_suggestion(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/rules.rs
10
src/rules.rs
|
|
@ -1,11 +1,13 @@
|
|||
use crate::suggestions::*;
|
||||
use crate::shell::Data;
|
||||
use pay_respects_parser::parse_rules;
|
||||
|
||||
pub fn match_pattern(
|
||||
executable: &str,
|
||||
last_command: &str,
|
||||
error_msg: &str,
|
||||
shell: &str,
|
||||
) -> Option<String> {
|
||||
data: &mut Data
|
||||
) {
|
||||
let error_msg = &data.error.clone();
|
||||
let shell = &data.shell.clone();
|
||||
let last_command = &data.command.clone();
|
||||
parse_rules!("rules");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::replaces;
|
||||
use crate::suggestions::*;
|
||||
use crate::shell::Data;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct Rule {
|
||||
|
|
@ -14,16 +15,19 @@ struct MatchError {
|
|||
|
||||
pub fn runtime_match(
|
||||
executable: &str,
|
||||
last_command: &str,
|
||||
error_msg: &str,
|
||||
shell: &str,
|
||||
) -> Option<String> {
|
||||
data: &mut Data,
|
||||
) {
|
||||
let file = get_rule(executable);
|
||||
file.as_ref()?;
|
||||
if file.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let file = std::fs::read_to_string(file.unwrap()).unwrap();
|
||||
let rule: Rule = toml::from_str(&file).unwrap();
|
||||
let split_command = split_command(last_command);
|
||||
let split_command = &data.split.clone();
|
||||
let shell = &data.shell.clone();
|
||||
let last_command = &data.command.clone();
|
||||
let error_msg = &data.error.clone();
|
||||
|
||||
let mut pure_suggest;
|
||||
|
||||
|
|
@ -79,13 +83,11 @@ pub fn runtime_match(
|
|||
if pure_suggest.contains("{{command}}") {
|
||||
pure_suggest = pure_suggest.replace("{{command}}", last_command);
|
||||
}
|
||||
return eval_suggest(&pure_suggest, last_command, error_msg, shell);
|
||||
data.add_candidate(&eval_suggest(&pure_suggest, last_command, error_msg, shell));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn eval_condition(
|
||||
|
|
@ -108,7 +110,7 @@ fn eval_condition(
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_suggest(suggest: &str, last_command: &str, error_msg: &str, shell: &str) -> Option<String> {
|
||||
fn eval_suggest(suggest: &str, last_command: &str, error_msg: &str, shell: &str) -> String {
|
||||
let mut suggest = suggest.to_owned();
|
||||
if suggest.contains("{{command}}") {
|
||||
suggest = suggest.replace("{{command}}", "{last_command}");
|
||||
|
|
@ -130,7 +132,7 @@ fn eval_suggest(suggest: &str, last_command: &str, error_msg: &str, shell: &str)
|
|||
suggest = suggest.replace(&tag, &value);
|
||||
}
|
||||
|
||||
Some(suggest)
|
||||
suggest
|
||||
}
|
||||
|
||||
fn get_rule(executable: &str) -> Option<String> {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub struct Data {
|
|||
pub shell: String,
|
||||
pub command: String,
|
||||
pub suggest: Option<String>,
|
||||
pub candidates: Vec<String>,
|
||||
pub split: Vec<String>,
|
||||
pub alias: Option<HashMap<String, String>>,
|
||||
pub privilege: Option<String>,
|
||||
|
|
@ -43,6 +44,7 @@ impl Data {
|
|||
shell,
|
||||
command,
|
||||
suggest: None,
|
||||
candidates: vec![],
|
||||
alias,
|
||||
split: vec![],
|
||||
privilege: None,
|
||||
|
|
@ -110,6 +112,13 @@ impl Data {
|
|||
self.suggest = Some(suggest.to_string());
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_candidate(&mut self, candidate: &str) {
|
||||
let candidate = candidate.trim();
|
||||
if candidate != self.command {
|
||||
self.candidates.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_command(command: &str) -> Vec<String> {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub fn highlight_difference(
|
|||
shell: &str,
|
||||
suggested_command: &str,
|
||||
last_command: &str,
|
||||
difference_only: bool,
|
||||
) -> Option<String> {
|
||||
// let replaced_newline = suggested_command.replace('\n', r" {{newline}} ");
|
||||
let mut split_suggested_command = split_command(suggested_command);
|
||||
|
|
@ -42,7 +43,9 @@ pub fn highlight_difference(
|
|||
}
|
||||
for old in &old_entries {
|
||||
if old == entry {
|
||||
*entry = entry.blue().to_string();
|
||||
if !difference_only {
|
||||
*entry = entry.blue().to_string();
|
||||
}
|
||||
continue 'next;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,50 +3,40 @@ use std::process::{exit, Stdio};
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use colored::Colorize;
|
||||
use inquire::*;
|
||||
use regex_lite::Regex;
|
||||
|
||||
use crate::files::{get_best_match_file, get_path_files};
|
||||
use crate::rules::match_pattern;
|
||||
use crate::shell::{shell_evaluated_commands, Data};
|
||||
use crate::style::highlight_difference;
|
||||
|
||||
pub fn suggest_command(data: &Data) -> Option<String> {
|
||||
let shell = &data.shell;
|
||||
let command = &data.command;
|
||||
let split_command = &data.split;
|
||||
let executable = data.split[0].as_str();
|
||||
let error = &data.error;
|
||||
let privilege = &data.privilege;
|
||||
pub fn suggest_candidates(data: &mut Data) {
|
||||
let executable = &data.split[0].to_string();
|
||||
let privilege = &data.privilege.clone();
|
||||
|
||||
if privilege.is_none() {
|
||||
let suggest = match_pattern("_PR_privilege", command, error, shell);
|
||||
if suggest.is_some() {
|
||||
return suggest;
|
||||
}
|
||||
}
|
||||
|
||||
let suggest = match_pattern(executable, command, error, shell);
|
||||
if suggest.is_some() {
|
||||
return suggest;
|
||||
}
|
||||
|
||||
let suggest = match_pattern("_PR_general", command, error, shell);
|
||||
if suggest.is_some() {
|
||||
return suggest;
|
||||
match_pattern("_PR_privilege", data);
|
||||
}
|
||||
match_pattern(executable, data);
|
||||
match_pattern("_PR_general", data);
|
||||
|
||||
#[cfg(feature = "runtime-rules")]
|
||||
{
|
||||
use crate::runtime_rules::runtime_match;
|
||||
let suggest = runtime_match(executable, command, error, shell);
|
||||
if suggest.is_some() {
|
||||
return suggest;
|
||||
}
|
||||
runtime_match(executable, data);
|
||||
}
|
||||
|
||||
#[cfg(feature = "request-ai")]
|
||||
{
|
||||
if !data.candidates.is_empty() {
|
||||
return;
|
||||
}
|
||||
use crate::requests::ai_suggestion;
|
||||
use textwrap::{fill, termwidth};
|
||||
let command = &data.command;
|
||||
let split_command = &data.split;
|
||||
let error = &data.error.clone();
|
||||
|
||||
// skip for commands with no arguments,
|
||||
// very likely to be an error showing the usage
|
||||
|
|
@ -60,12 +50,52 @@ pub fn suggest_command(data: &Data) -> Option<String> {
|
|||
|
||||
eprintln!("{}\n{}\n", warn, note);
|
||||
let command = suggest.command;
|
||||
return Some(command);
|
||||
data.add_candidate(&command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
pub fn select_candidate(data: &mut Data) {
|
||||
let candidates = &data.candidates;
|
||||
if candidates.len() == 1 {
|
||||
let suggestion = candidates[0].to_string();
|
||||
let highlighted = highlight_difference(&data.shell, &suggestion, &data.command, false).unwrap();
|
||||
eprintln!("{}\n", highlighted);
|
||||
let confirm = format!("[{}]", t!("confirm-yes")).green();
|
||||
eprintln!("{}: {} {}", t!("confirm"), confirm, "[Ctrl+C]".red());
|
||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||
data.update_suggest(&suggestion);
|
||||
data.expand_suggest();
|
||||
} else {
|
||||
let mut highlight_candidates = candidates
|
||||
.iter()
|
||||
.map(|candidate| highlight_difference(&data.shell, candidate, &data.command, true).unwrap())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
for candidate in highlight_candidates.iter_mut() {
|
||||
let lines = candidate.lines().collect::<Vec<&str>>();
|
||||
let mut formated = String::new();
|
||||
for (j, line) in lines.iter().enumerate() {
|
||||
if j == 0 {
|
||||
formated = line.to_string();
|
||||
} else {
|
||||
formated = format!("{}\n {}", formated, line);
|
||||
}
|
||||
}
|
||||
*candidate = formated;
|
||||
}
|
||||
|
||||
let ans = Select::new("Select a suggestion:", highlight_candidates.clone())
|
||||
.prompt()
|
||||
.unwrap();
|
||||
let pos = highlight_candidates.iter().position(|x| x == &ans).unwrap();
|
||||
let suggestion = candidates[pos].to_string();
|
||||
data.update_suggest(&suggestion);
|
||||
data.expand_suggest();
|
||||
}
|
||||
|
||||
data.candidates.clear();
|
||||
}
|
||||
|
||||
pub fn check_executable(shell: &str, executable: &str) -> bool {
|
||||
|
|
@ -244,12 +274,7 @@ pub fn compare_string(a: &str, b: &str) -> usize {
|
|||
matrix[a.chars().count()][b.chars().count()]
|
||||
}
|
||||
|
||||
pub fn confirm_suggestion(data: &Data, highlighted: &str) -> Result<(), String> {
|
||||
eprintln!("{}\n", highlighted);
|
||||
let confirm = format!("[{}]", t!("confirm-yes")).green();
|
||||
eprintln!("{}: {} {}", t!("confirm"), confirm, "[Ctrl+C]".red());
|
||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||
|
||||
pub fn confirm_suggestion(data: &Data) -> Result<(), String> {
|
||||
let shell = &data.shell;
|
||||
let command = &data.suggest.clone().unwrap();
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::process::Command;
|
|||
use std::process::Stdio;
|
||||
|
||||
pub fn get_package_manager(shell: &str) -> Option<String> {
|
||||
let package_managers = vec!["pacman", "apt", "dnf"];
|
||||
let package_managers = vec!["pacman"];
|
||||
|
||||
for package_manager in package_managers {
|
||||
let success = Command::new(shell)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue