From 7677adfafe323315b5f8df7e28142b133046778e Mon Sep 17 00:00:00 2001 From: Justine Date: Tue, 14 Mar 2023 22:27:43 +0100 Subject: [PATCH 1/2] Readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 20d7815..ec6d01a 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ TODO (Reformat): TODO (features): * Add a && +TODO (Bugz): +* Autocomplete shits itself when typing a path starting with / + ## sqishrc (Config) See the included sqishrc file included for comments, and copy it as ~/.sqishrc for use (optionnal). From f333069a32d4278717365ece6a3b32ae676c1547 Mon Sep 17 00:00:00 2001 From: Justine Date: Mon, 1 May 2023 13:42:56 +0200 Subject: [PATCH 2/2] Fixed the ./ bug --- src/shell/autocomplete.rs | 47 ++++++-- src/shell/commandrun.rs | 236 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 src/shell/commandrun.rs diff --git a/src/shell/autocomplete.rs b/src/shell/autocomplete.rs index d14913d..8e177ff 100644 --- a/src/shell/autocomplete.rs +++ b/src/shell/autocomplete.rs @@ -25,6 +25,8 @@ pub struct Search { local_search: bool, //the kind of search search_type: SearchType, + //the original command + original: String, } impl Search { @@ -83,6 +85,7 @@ impl Search { searchee: searchee, local_search: local, search_type: search_type, + original: String::from(input), }; return Ok(s); } @@ -141,6 +144,8 @@ fn autocomplete_file(search: &Search) -> std::io::Result<(String, String)> { let mut last_found = String::new(); let mut results = Vec::new(); + + //This is pretty messy. for file in fs::read_dir(search.search_path.clone())? { let filepath = file.unwrap().path(); let filename = filepath @@ -192,18 +197,44 @@ fn autocomplete_file(search: &Search) -> std::io::Result<(String, String)> { *&mut last_found = search.searchee.clone(); } - //Handle the path when tabbing in a long path, zsh-like - //then return the value - let xxx = &search.search_path.clone().into_os_string().into_string().unwrap(); - let mut return_val = String::new(); - if !&search.local_search && !&last_found.contains(&xxx.as_str()) { - *&mut return_val = format!("{}{}{}", &search.command, &search.search_path.display(), last_found); - } else { - *&mut return_val = format!("{}{}", &search.command, last_found); + //Not looking for anything in particular, this is a special case. + if search.searchee == "" { + return Ok((String::from(&search.original), all_res)); } + + //Remote search... is annoying + if !search.local_search { + last_found = last_found.replace("./", ""); + return Ok((format!("{}{}{}", search.command, search.search_path.display(), last_found), String::new())); + } + + last_found = last_found.replace("./", ""); + + //Otherwise... Handling the return as gracefully as I can + let mut return_val = String::new(); + if all_res.len() > 0 { + let ret_string = replace_last_word(&String::from(&search.original), last_found); + *&mut return_val = format!("{}", ret_string); + } else { + *&mut return_val = format!("{}", &search.original); + } + return Ok((return_val, all_res)); } +//Matches on space or slash +fn replace_last_word(sentence: &String, replacement: String) -> String { + let lastword = sentence.split(|c| c == ' ' || c == '/').last().unwrap_or(""); + let mut work_sent = sentence.clone(); + work_sent.truncate(sentence.len() - lastword.len()); + work_sent.push_str(&replacement); + return work_sent; +} + +//fn return_last_word(sentence: &String) -> String { +// let lastword = sentence.split(|c| c == ' ' || c == '/').last().unwrap_or(""); +// return String::from(lastword); +//} fn autocomplete_cmd(input: &String, search: &Search) -> (String, String) { let found_bins = find_bin(input); diff --git a/src/shell/commandrun.rs b/src/shell/commandrun.rs new file mode 100644 index 0000000..e0b3b9b --- /dev/null +++ b/src/shell/commandrun.rs @@ -0,0 +1,236 @@ +use std::io::Stdout; +use std::process::Command; +use std::io::stdin; +use std::io::{stdout}; +use std::io::Write; +use std::path::Path; +use std::env; +use std::str; +use std::fs::File; +use std::process::Stdio; +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::IntoRawMode; +use termion::raw::RawTerminal; +use termion::{color, cursor}; +use termion; +use unicode_segmentation::UnicodeSegmentation; +use std::collections::HashMap; + +mod history; +use crate::shell::history::*; + +///Used to unify command output and error handling +///across functions +pub struct CmdOutput { + pub outp: String, + pub rc: i16, +} + +impl CmdOutput { + pub fn from_values(output: String, rc: i16) -> CmdOutput { + let myoutp = CmdOutput { + outp: output, + rc: rc, + }; + return myoutp; + } + pub fn new_empty() -> CmdOutput { + let outp = CmdOutput { + outp: String::new(), + rc: 0, + }; + return outp; + } +} + +fn change_dir(args: &Vec, previous_command: &mut Option) -> Result { + let homedir = dirs::home_dir().unwrap().into_os_string().into_string().unwrap(); + //let new_dir = args.peekable().peek().map_or(homedir.as_str(), |x| *x); + let mut new_dir = String::new(); + if args.len() > 0 { + *&mut new_dir = args.join(" "); + } else { + *&mut new_dir = String::from(homedir.as_str()); + } + + let root = Path::new(&new_dir); + if let Err(e) = env::set_current_dir(&root) { + return Err(CmdOutput::from_values( + format!("Error setting directory to {}, got {:?}", + new_dir, e), -1)); + }; + *previous_command = None; + return Ok(CmdOutput::new_empty()); +} + +fn print_hist(previous_command: &mut Option, command_next: bool) -> Result { + //let stdout = Stdio::inherit(); + let res = get_history(); + match res { + Ok(r) => { + let filepath = dirs::home_dir().unwrap().join(".history.sqish"); + let file = File::open(filepath).unwrap(); + *previous_command = Some(Stdio::from(file)); + //if !commands.peek().is_some() { + if !command_next { + print!("{}", r); + } + }, + Err(e) => { + return Err(CmdOutput::from_values(format!("Could not print history, got {:?}", e), 255)); + }, + } + return Ok(CmdOutput::new_empty()); +} + + +fn process_line(input: &str) -> Result { + let mut commands = input.trim().split("|").peekable(); + let mut previous_command = None; + let mut resultat = String::new(); + let mut rc: i16 = 0; + + while let Some(command) = commands.next() { + let parts = match shell_words::split(&command.trim()) { + Ok(w) => w, + Err(e) => { + return Err(CmdOutput::from_values(format!("Could not parse command {:?}", e), -1)); + }, + }; + let command = parts[0].as_str(); + let args = Vec::from(&parts[1..]); + + match command { + "cd" => { + change_dir(&args, &mut previous_command)?; + }, + "history" => { + let next = commands.peek().is_some(); + print_hist(&mut previous_command, next)?; + }, + command => { + if commands.peek().is_some() { + let stdin = match previous_command { + None => Stdio::inherit(), + Some(o) => o, + }; + let stdout = Stdio::piped(); + + let output = Command::new(command) + .args(args) + .stdout(stdout) + .stdin(stdin) + .spawn(); + + match output { + Ok(o) => previous_command = Some(Stdio::from(o.stdout.unwrap())), + Err(e) => { + previous_command = None; + eprintln!(" Err: {}", e); + }, + }; + } else { + let stdin = match previous_command { + None => Stdio::inherit(), + Some(o) => o, + }; + + print!("\r\n"); + + let child = match Command::new(command) + .args(args) + .stdin(stdin) + .spawn() { + Ok(h) => h, + Err(_) => { + let err_string = format!("Not found : {}", command); + return Err(CmdOutput::from_values(err_string, -1)); + }, + }; + + let output = child + .wait_with_output() + .expect("Failed to wait on child"); + let status = output.status + .code() + .unwrap_or(255) + .to_string(); + previous_command = None; + let format_res = format!("{}", + status); + *&mut rc = format_res.parse::().unwrap(); + *&mut resultat = String::from_utf8(output.stdout).unwrap(); + //let _ = &mut resultat.push_str(&format_res); + } + }, + }; + } + //return resultat; + return Ok(CmdOutput::from_values(resultat, rc)); +} + +pub fn run_cmd(mycommand: &mut String, + current_number: &mut i32, + conf: &mut SqishConf, + stdout: &mut RawTerminal) { + //NORMAL CMD + if (mycommand != &String::from("")) && (mycommand != &String::from("exit")) { + //Run the command + let comm = replace_signs(&mycommand); + RawTerminal::suspend_raw_mode(&stdout); + let res = handle_input(&comm); + //Display the results + match res { + Ok(_) => (), + Err(e) => { + println!("\r\n{}Got error {}, RC : {:?}{}", + color::Fg(color::Red), + e.outp, + e.rc, + color::Fg(color::Reset)); + }, + }; + //Prepare for a new command + RawTerminal::activate_raw_mode(&stdout); + mycommand.clear(); + conf.update_prompt(get_curr_history_number()); + write!(stdout, "{}", conf.promptline).unwrap(); + stdout.flush(); + *current_number += 1; + //EXITTING + } else if mycommand == &String::from("exit") { + write!(stdout, "\r\nThanks for using Sqish!\r\nSayonara \r\n"); + RawTerminal::suspend_raw_mode(&stdout); + std::process::exit(0); + //EMPTY LINE + } else { + conf.update_prompt(get_curr_history_number()); + write!(stdout, "\r\n{}", conf.promptline).unwrap(); + } + stdout.flush(); +} + +pub fn handle_input(line: &str) -> Result { + //history callback + if (line.len() > 0) && (line.starts_with('!')) { + let command_found = treat_history_callback(line); + match command_found { + Some(c) => { + write_to_history(&c); + return process_line(&c); + }, + None => return Err(CmdOutput::from_values( + String::from("No such value"), + 255)), + }; + //Regular command + } else if line.len() > 0 { + write_to_history(line); + return process_line(line); + //Nothing + } else { + //Command was empty, new line + return Ok(CmdOutput::new_empty()); + } +}