sqishrc ok
This commit is contained in:
parent
2dd83d0097
commit
ac775f8ce0
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -76,6 +76,12 @@ version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
@ -178,6 +184,7 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
"users",
|
||||
"which",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -341,3 +348,12 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
@ -15,3 +15,4 @@ termion = "2.0.1"
|
||||
unicode-segmentation = "1.6.0"
|
||||
which = { version = "4.4.0", features = ["regex"] }
|
||||
regex = "1.7.1"
|
||||
yaml-rust = "0.4.5"
|
||||
|
13
README.md
13
README.md
@ -1,11 +1,12 @@
|
||||
# Sqish
|
||||
Rust Shell. This is an attempt to create a simple shell in Rust, because why not.
|
||||
|
||||
!!! LOOK into the pty crate to handle pseudoterminals such as in top or ssh
|
||||
TO DO:
|
||||
* Add prompt RGB support
|
||||
* Add hotkeys in sqishrc (albeit in the same manner as per colors, the conf Struct already has an hashmap field for it)
|
||||
* Add aliases in sqishrc (same old same old)
|
||||
|
||||
## sqishrc (Config)
|
||||
See the included sqishrc.yaml.example file included, and copy it as ~/.sqishrc.yaml
|
||||
|
||||
My starting point is [this article](https://www.joshmcguigan.com/blog/build-your-own-shell-rust/) which is excellent. I will start with the final form of a shell given in the article and try to reformat it:
|
||||
|
||||
## To fix
|
||||
* Autocomplete : when nothing found, return input
|
||||
* Autocomplete : Weird permission issues sometimes (ignore unallowed files and folders)
|
||||
* Bug with find_common_chars (if I type Cargo.t and press tab, we can't choose between Cargo.toml and Cargo.lock)
|
||||
|
24
sqishrc.yaml
Normal file
24
sqishrc.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
#This is an example sqishrc. to be copied and modified as ~/.sqish.yaml (not .yml,
|
||||
#I can't be bothered to deal with 2 different extensions).
|
||||
#
|
||||
#Prompt: Defines the promptline. Values are defined in all-caps, between $ and _.
|
||||
#Possible values:
|
||||
#HISTNUMBER: Current history number. Useful for !callback
|
||||
#USER : Username.
|
||||
#HOSTNAME : Hostname of the machine.
|
||||
#DIR : Name of the current directory.
|
||||
#
|
||||
#Colors : All colors come from Termion.
|
||||
#Possible values:
|
||||
#RESET : Resets the color. Don't forget to add one in the end of your prompt !
|
||||
#GREEN
|
||||
#BLUE
|
||||
#RED
|
||||
#BLACK
|
||||
#WHITE
|
||||
#CYAN
|
||||
#LBLACK (light black, a dark gray in fact)
|
||||
#Will definitely add RGB support, stay tuned.
|
||||
prompt: "$COLORLBLACK_ !$HISTNUMBER_$COLORCYAN_[$USER_@$HOSTNAME_] $DIR_> $COLORRESET_"
|
||||
|
70
src/lib.rs
70
src/lib.rs
@ -16,6 +16,7 @@ pub mod shell {
|
||||
use termion::color;
|
||||
use termion;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod history;
|
||||
use crate::shell::history::*;
|
||||
@ -23,8 +24,8 @@ pub mod shell {
|
||||
mod autocomplete;
|
||||
use crate::shell::autocomplete::*;
|
||||
|
||||
mod prompt;
|
||||
use crate::shell::prompt::*;
|
||||
mod config;
|
||||
use crate::shell::config::*;
|
||||
|
||||
//used for home directory
|
||||
extern crate dirs;
|
||||
@ -58,11 +59,11 @@ pub mod shell {
|
||||
Err(e) => eprintln!(" Err: {}", e),
|
||||
}
|
||||
},
|
||||
"ssh" => {
|
||||
let ssh_args = args.peekable().peek().map_or(" ", |x| *x);
|
||||
let mut child = Command::new("ssh").arg(ssh_args).spawn().unwrap();
|
||||
let _ = child.wait().unwrap();
|
||||
},
|
||||
// "ssh" => {
|
||||
// let ssh_args = args.peekable().peek().map_or(" ", |x| *x);
|
||||
// let mut child = Command::new("ssh").arg(ssh_args).spawn().unwrap();
|
||||
// let _ = child.wait().unwrap();
|
||||
// },
|
||||
command => {
|
||||
if commands.peek().is_some() {
|
||||
let stdin = match previous_command {
|
||||
@ -230,14 +231,30 @@ pub mod shell {
|
||||
//Used in conjunction with Up arrow to go back in history
|
||||
//Resetted by pressing Enter
|
||||
let mut current_number = get_curr_history_number();
|
||||
let mut prompt = build_prompt(¤t_number);
|
||||
|
||||
//Used to track the location of the cursor inside the command
|
||||
let mut current_pos: usize = 0;
|
||||
let mut max_pos: usize = 0;
|
||||
|
||||
let mut conf = match SqishConf::from_sqishrc() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
let ret_line = format!("Could not build conf, got {}\r\nUsing default\r\n", e);
|
||||
write!(stdout, "{}", ret_line);
|
||||
let conf = SqishConf {
|
||||
promptline: String::from("$COLORGREEN_ [$USER_@$HOSTNAME_] "),
|
||||
promptline_base: String::from("$COLORGREEN_ [$USER_@$HOSTNAME_] "),
|
||||
aliases: HashMap::new(),
|
||||
hotkeys: HashMap::new(),
|
||||
};
|
||||
conf
|
||||
},
|
||||
};
|
||||
&conf.update_prompt(get_curr_history_number());
|
||||
|
||||
|
||||
//Initialize
|
||||
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
||||
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", conf.promptline);
|
||||
stdout.flush();
|
||||
|
||||
for c in stdin.keys() {
|
||||
@ -248,44 +265,42 @@ pub mod shell {
|
||||
if *&mycommand.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Search
|
||||
*&mut mycommand = replace_signs(&mycommand);
|
||||
let (res, list) = Search::build(&mycommand).unwrap().autocomplete();
|
||||
write!(stdout, "\r\n{}\r\n", list);
|
||||
mycommand.clear();
|
||||
mycommand.push_str(&res);
|
||||
max_pos = res.len();
|
||||
current_pos = max_pos;
|
||||
write!(stdout, "\r\n{}{}", prompt, res);
|
||||
write!(stdout, "\r\n{}{}", conf.promptline, res);
|
||||
stdout.flush();
|
||||
}
|
||||
|
||||
Key::Char('\n') => {
|
||||
current_number = get_curr_history_number();
|
||||
prompt = build_prompt(¤t_number);
|
||||
current_pos = 0;
|
||||
max_pos = 0;
|
||||
if (mycommand != String::from("\n")) && (mycommand != String::from("exit")) {
|
||||
let comm = replace_signs(&mycommand);
|
||||
RawTerminal::suspend_raw_mode(&stdout);
|
||||
let res = handle_input(&comm);
|
||||
let _res = handle_input(&comm);
|
||||
RawTerminal::activate_raw_mode(&stdout);
|
||||
mycommand.clear();
|
||||
//Proper printing
|
||||
for line in res.split('\n') {
|
||||
if line != "\r" {
|
||||
write!(stdout, "\r\n{}", line);
|
||||
}
|
||||
}
|
||||
current_number = get_curr_history_number();
|
||||
prompt = build_prompt(¤t_number);
|
||||
write!(stdout, "{}", prompt).unwrap();
|
||||
//for line in res.split('\n') {
|
||||
// if line != "\r" {
|
||||
// write!(stdout, "\r\n{}", line);
|
||||
// }
|
||||
//}
|
||||
&conf.update_prompt(get_curr_history_number());
|
||||
write!(stdout, "\r\n{}", conf.promptline).unwrap();
|
||||
stdout.flush();
|
||||
} else if mycommand == String::from("exit") {
|
||||
write!(stdout, "\r\n Sayonara \r\n");
|
||||
break;
|
||||
} else {
|
||||
write!(stdout, "\r\n{}", prompt).unwrap();
|
||||
&conf.update_prompt(get_curr_history_number());
|
||||
write!(stdout, "\r\n{}", conf.promptline).unwrap();
|
||||
stdout.flush();
|
||||
}
|
||||
},
|
||||
@ -300,9 +315,7 @@ pub mod shell {
|
||||
max_pos = 0;
|
||||
mycommand.clear();
|
||||
write!(stdout, "\r\n--CANCEL--\r\n");
|
||||
current_number = get_curr_history_number();
|
||||
prompt = build_prompt(¤t_number);
|
||||
write!(stdout, "{}", prompt);
|
||||
write!(stdout, "{}", conf.promptline);
|
||||
},
|
||||
|
||||
Key::Ctrl('t') => {
|
||||
@ -327,6 +340,7 @@ pub mod shell {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Key::Char(c) => {
|
||||
//if at the end of the command...
|
||||
if current_pos == max_pos {
|
||||
@ -350,7 +364,7 @@ pub mod shell {
|
||||
},
|
||||
|
||||
Key::Delete => {
|
||||
if current_pos >= 0 && current_pos < max_pos {
|
||||
if current_pos < max_pos {
|
||||
mycommand.remove(current_pos);
|
||||
if current_pos > 0 {
|
||||
current_pos -= 1;
|
||||
@ -388,7 +402,6 @@ pub mod shell {
|
||||
};
|
||||
mycommand = lastcmd.trim().to_string();
|
||||
(max_pos, current_pos) = get_cmd_curs_pos(&mycommand);
|
||||
//let prompt = build_prompt();
|
||||
write!(stdout, "{}", mycommand);
|
||||
},
|
||||
|
||||
@ -401,7 +414,6 @@ pub mod shell {
|
||||
};
|
||||
mycommand = lastcmd.trim().to_string();
|
||||
(max_pos, current_pos) = get_cmd_curs_pos(&mycommand);
|
||||
//let prompt = build_prompt();
|
||||
write!(stdout, "{}", mycommand);
|
||||
},
|
||||
|
||||
|
123
src/shell/config.rs
Normal file
123
src/shell/config.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use users::{get_user_by_uid, get_current_uid};
|
||||
use termion::color;
|
||||
use std::{env, fs};
|
||||
use std::collections::HashMap;
|
||||
|
||||
extern crate dirs;
|
||||
|
||||
extern crate yaml_rust;
|
||||
use yaml_rust::{YamlLoader};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SqishConf {
|
||||
pub promptline: String,
|
||||
pub promptline_base: String,
|
||||
pub aliases: HashMap<String, String>,
|
||||
pub hotkeys: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl SqishConf {
|
||||
pub fn from_sqishrc() -> Result<SqishConf, String> {
|
||||
let mut conf_path = match dirs::home_dir() {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return Err(String::from("Home dir could not be determined."));
|
||||
},
|
||||
};
|
||||
conf_path.push(".sqishrc.yaml");
|
||||
|
||||
let sqishrc_file = match fs::read_to_string(conf_path) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(String::from("Could not read ~/.sqishrc.yaml"));
|
||||
},
|
||||
};
|
||||
|
||||
let sqishrc = match YamlLoader::load_from_str(&sqishrc_file) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(String::from("sqishrc is not valid yaml"));
|
||||
},
|
||||
};
|
||||
|
||||
let sqishrc = &sqishrc[0];
|
||||
let out_str = String::from(sqishrc["prompt"].as_str().unwrap());
|
||||
|
||||
let mut out_conf = SqishConf {
|
||||
promptline: out_str.clone(),
|
||||
promptline_base: out_str,
|
||||
aliases: HashMap::new(),
|
||||
hotkeys: HashMap::new(),
|
||||
};
|
||||
|
||||
out_conf.handle_colors();
|
||||
|
||||
return Ok(out_conf);
|
||||
}
|
||||
|
||||
#[warn(unused_assignments)]
|
||||
pub fn update_prompt(&mut self, hist_nbr: i32) {
|
||||
|
||||
let user = String::from(
|
||||
get_user_by_uid(get_current_uid())
|
||||
.unwrap()
|
||||
.name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let host = String::from(
|
||||
gethostname::gethostname()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let current_dir = String::from(
|
||||
env::current_dir()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let s = current_dir.split('/');
|
||||
let dir_last = String::from(
|
||||
s.last()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let hist_nbr = hist_nbr.to_string();
|
||||
|
||||
let mut prompt = self.promptline_base.replace("$USER_", &user);
|
||||
prompt = prompt.replace("$HOSTNAME_", &host);
|
||||
prompt = prompt.replace("$DIR_", &dir_last);
|
||||
prompt = prompt.replace("$HISTNUMBER_", &hist_nbr);
|
||||
prompt = prompt.to_owned();
|
||||
self.promptline = prompt;
|
||||
self.handle_colors();
|
||||
}
|
||||
|
||||
|
||||
fn handle_colors(&mut self) {
|
||||
let reset = format!("{}", color::Fg(color::Reset));
|
||||
let green = format!("{}", color::Fg(color::Green));
|
||||
let blue = format!("{}", color::Fg(color::Blue));
|
||||
let red = format!("{}", color::Fg(color::Red));
|
||||
let black = format!("{}", color::Fg(color::Black));
|
||||
let white = format!("{}", color::Fg(color::White));
|
||||
let cyan = format!("{}", color::Fg(color::Cyan));
|
||||
let lightblack = format!("{}", color::Fg(color::LightBlack));
|
||||
|
||||
let mut prompt = self.promptline.replace("$COLORGREEN_", &green);
|
||||
prompt = prompt.replace("$COLORBLUE_", &blue);
|
||||
prompt = prompt.replace("$COLORRED_", &red);
|
||||
prompt = prompt.replace("$COLORBLACK_", &black);
|
||||
prompt = prompt.replace("$COLORWHITE_", &white);
|
||||
prompt = prompt.replace("$COLORCYAN_", &cyan);
|
||||
prompt = prompt.replace("$COLORLBLACK_", &lightblack);
|
||||
prompt = prompt.replace("$COLORRESET_", &reset);
|
||||
let promptown = prompt.to_owned();
|
||||
self.promptline = promptown;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
use users::{get_user_by_uid, get_current_uid};
|
||||
use termion::color;
|
||||
use std::env;
|
||||
|
||||
pub fn build_prompt(curr_number: &i32) -> String {
|
||||
|
||||
let user = String::from(
|
||||
get_user_by_uid(get_current_uid())
|
||||
.unwrap()
|
||||
.name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let host = String::from(
|
||||
gethostname::gethostname()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let current_dir = String::from(
|
||||
env::current_dir()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let s = current_dir.split('/');
|
||||
let dir_last = String::from(
|
||||
s.last()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ",
|
||||
color::Fg(color::LightBlack),
|
||||
curr_number,
|
||||
color::Fg(color::LightCyan),
|
||||
user, host, dir_last,
|
||||
color::Fg(color::Reset)));
|
||||
return prompt;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user