Implemented functions and aliases
This commit is contained in:
19
src/builtin/alias.rs
Normal file
19
src/builtin/alias.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn alias(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let rule = node.into_rule();
|
||||||
|
if let NdRule::Command { argv, redirs: _ } = rule {
|
||||||
|
let argv = argv.drop_first();
|
||||||
|
let mut argv_iter = argv.into_iter();
|
||||||
|
while let Some(arg) = argv_iter.next() {
|
||||||
|
let arg_raw = arg.to_string();
|
||||||
|
if let Some((alias,body)) = arg_raw.split_once('=') {
|
||||||
|
let clean_body = trim_quotes(&body);
|
||||||
|
shenv.logic_mut().set_alias(alias, &clean_body);
|
||||||
|
} else {
|
||||||
|
return Err(ShErr::full(ShErrKind::SyntaxErr, "Expected an assignment in alias args", arg.span().clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { unreachable!() }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ pub fn continue_job(node: Node, shenv: &mut ShEnv, fg: bool) -> ShResult<()> {
|
|||||||
let curr_job_id = if let Some(id) = read_jobs(|j| j.curr_job()) {
|
let curr_job_id = if let Some(id) = read_jobs(|j| j.curr_job()) {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
return Err(ShErr::full(ShErrKind::ExecFail, "No jobs found".into(), blame))
|
return Err(ShErr::full(ShErrKind::ExecFail, "No jobs found", blame))
|
||||||
};
|
};
|
||||||
|
|
||||||
let tabid = match argv_s.next() {
|
let tabid = match argv_s.next() {
|
||||||
@@ -66,7 +66,7 @@ fn parse_job_id(arg: &str, blame: Span) -> ShResult<usize> {
|
|||||||
});
|
});
|
||||||
match result {
|
match result {
|
||||||
Some(id) => Ok(id),
|
Some(id) => Ok(id),
|
||||||
None => Err(ShErr::full(ShErrKind::InternalErr,"Found a job but no table id in parse_job_id()".into(),blame))
|
None => Err(ShErr::full(ShErrKind::InternalErr,"Found a job but no table id in parse_job_id()",blame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if arg.chars().all(|ch| ch.is_ascii_digit()) {
|
} else if arg.chars().all(|ch| ch.is_ascii_digit()) {
|
||||||
@@ -86,7 +86,7 @@ fn parse_job_id(arg: &str, blame: Span) -> ShResult<usize> {
|
|||||||
|
|
||||||
match result {
|
match result {
|
||||||
Some(id) => Ok(id),
|
Some(id) => Ok(id),
|
||||||
None => Err(ShErr::full(ShErrKind::InternalErr,"Found a job but no table id in parse_job_id()".into(),blame))
|
None => Err(ShErr::full(ShErrKind::InternalErr,"Found a job but no table id in parse_job_id()",blame))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ShErr::full(ShErrKind::SyntaxErr,format!("Invalid fd arg: {}", arg),blame))
|
Err(ShErr::full(ShErrKind::SyntaxErr,format!("Invalid fd arg: {}", arg),blame))
|
||||||
@@ -103,7 +103,7 @@ pub fn jobs(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
let arg_s = arg.to_string();
|
let arg_s = arg.to_string();
|
||||||
let mut chars = arg_s.chars().peekable();
|
let mut chars = arg_s.chars().peekable();
|
||||||
if chars.peek().is_none_or(|ch| *ch != '-') {
|
if chars.peek().is_none_or(|ch| *ch != '-') {
|
||||||
return Err(ShErr::full(ShErrKind::SyntaxErr, "Invalid flag in jobs call".into(), arg.span().clone()))
|
return Err(ShErr::full(ShErrKind::SyntaxErr, "Invalid flag in jobs call", arg.span().clone()))
|
||||||
}
|
}
|
||||||
chars.next();
|
chars.next();
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
@@ -113,7 +113,7 @@ pub fn jobs(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
'n' => JobCmdFlags::NEW_ONLY,
|
'n' => JobCmdFlags::NEW_ONLY,
|
||||||
'r' => JobCmdFlags::RUNNING,
|
'r' => JobCmdFlags::RUNNING,
|
||||||
's' => JobCmdFlags::STOPPED,
|
's' => JobCmdFlags::STOPPED,
|
||||||
_ => return Err(ShErr::full(ShErrKind::SyntaxErr, "Invalid flag in jobs call".into(), arg.span().clone()))
|
_ => return Err(ShErr::full(ShErrKind::SyntaxErr, "Invalid flag in jobs call", arg.span().clone()))
|
||||||
|
|
||||||
};
|
};
|
||||||
flags |= flag
|
flags |= flag
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ pub mod pwd;
|
|||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod jobctl;
|
pub mod jobctl;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
|
pub mod alias;
|
||||||
|
|
||||||
pub const BUILTINS: [&str;8] = [
|
pub const BUILTINS: [&str;9] = [
|
||||||
"echo",
|
"echo",
|
||||||
"cd",
|
"cd",
|
||||||
"pwd",
|
"pwd",
|
||||||
@@ -13,5 +14,6 @@ pub const BUILTINS: [&str;8] = [
|
|||||||
"fg",
|
"fg",
|
||||||
"bg",
|
"bg",
|
||||||
"jobs",
|
"jobs",
|
||||||
"read"
|
"read",
|
||||||
|
"alias"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ fn exec_list(list: Vec<(Option<CmdGuard>, Node)>, shenv: &mut ShEnv) -> ShResult
|
|||||||
}
|
}
|
||||||
log!(TRACE, "{:?}", *cmd.rule());
|
log!(TRACE, "{:?}", *cmd.rule());
|
||||||
match *cmd.rule() {
|
match *cmd.rule() {
|
||||||
NdRule::Command {..} if cmd.flags().contains(NdFlag::BUILTIN) => exec_builtin(cmd,shenv).try_blame(span)?,
|
NdRule::Command {..} => dispatch_command(cmd, shenv).try_blame(span)?,
|
||||||
NdRule::Command {..} => exec_cmd(cmd,shenv).try_blame(span)?,
|
|
||||||
NdRule::Subshell {..} => exec_subshell(cmd,shenv).try_blame(span)?,
|
NdRule::Subshell {..} => exec_subshell(cmd,shenv).try_blame(span)?,
|
||||||
|
NdRule::FuncDef {..} => exec_funcdef(cmd,shenv).try_blame(span)?,
|
||||||
NdRule::Assignment {..} => exec_assignment(cmd,shenv).try_blame(span)?,
|
NdRule::Assignment {..} => exec_assignment(cmd,shenv).try_blame(span)?,
|
||||||
NdRule::Pipeline {..} => exec_pipeline(cmd, shenv).try_blame(span)?,
|
NdRule::Pipeline {..} => exec_pipeline(cmd, shenv).try_blame(span)?,
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
@@ -58,6 +58,83 @@ fn exec_list(list: Vec<(Option<CmdGuard>, Node)>, shenv: &mut ShEnv) -> ShResult
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispatch_command(mut node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let mut is_builtin = false;
|
||||||
|
let mut is_func = false;
|
||||||
|
let mut is_subsh = false;
|
||||||
|
if let NdRule::Command { ref mut argv, redirs: _ } = node.rule_mut() {
|
||||||
|
*argv = expand_argv(argv.to_vec(), shenv);
|
||||||
|
let cmd = argv.first().unwrap().to_string();
|
||||||
|
if shenv.logic().get_function(&cmd).is_some() {
|
||||||
|
is_func = true;
|
||||||
|
} else if node.flags().contains(NdFlag::BUILTIN) {
|
||||||
|
is_builtin = true;
|
||||||
|
}
|
||||||
|
} else if let NdRule::Subshell { body: _, ref mut argv, redirs: _ } = node.rule_mut() {
|
||||||
|
*argv = expand_argv(argv.to_vec(), shenv);
|
||||||
|
is_subsh = true;
|
||||||
|
} else { unreachable!() }
|
||||||
|
|
||||||
|
if is_builtin {
|
||||||
|
exec_builtin(node, shenv)?;
|
||||||
|
} else if is_func {
|
||||||
|
exec_func(node, shenv)?;
|
||||||
|
} else if is_subsh {
|
||||||
|
exec_subshell(node, shenv)?;
|
||||||
|
} else {
|
||||||
|
exec_cmd(node, shenv)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_func(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let rule = node.into_rule();
|
||||||
|
if let NdRule::Command { argv, redirs } = rule {
|
||||||
|
let mut argv_iter = argv.into_iter();
|
||||||
|
let func_name = argv_iter.next().unwrap().to_string();
|
||||||
|
let body = shenv.logic().get_function(&func_name).unwrap().to_string();
|
||||||
|
let snapshot = shenv.clone();
|
||||||
|
shenv.vars_mut().reset_params();
|
||||||
|
while let Some(arg) = argv_iter.next() {
|
||||||
|
shenv.vars_mut().bpush_arg(&arg.to_string());
|
||||||
|
}
|
||||||
|
shenv.collect_redirs(redirs);
|
||||||
|
|
||||||
|
let lex_input = Rc::new(body);
|
||||||
|
let tokens = Lexer::new(lex_input).lex();
|
||||||
|
match Parser::new(tokens).parse() {
|
||||||
|
Ok(syn_tree) => {
|
||||||
|
match Executor::new(syn_tree, shenv).walk() {
|
||||||
|
Ok(_) => { /* yippee */ }
|
||||||
|
Err(e) => {
|
||||||
|
*shenv = snapshot;
|
||||||
|
return Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
*shenv = snapshot;
|
||||||
|
return Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*shenv = snapshot;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_funcdef(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let rule = node.into_rule();
|
||||||
|
if let NdRule::FuncDef { name, body } = rule {
|
||||||
|
let name_raw = name.to_string();
|
||||||
|
let name = name_raw.trim_end_matches("()");
|
||||||
|
let body_raw = body.to_string();
|
||||||
|
let body = body_raw[1..body_raw.len() - 1].trim();
|
||||||
|
|
||||||
|
shenv.logic_mut().set_function(name, body);
|
||||||
|
} else { unreachable!() }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
fn exec_subshell(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let snapshot = shenv.clone();
|
let snapshot = shenv.clone();
|
||||||
shenv.vars_mut().reset_params();
|
shenv.vars_mut().reset_params();
|
||||||
@@ -154,6 +231,7 @@ fn exec_builtin(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
"fg" => continue_job(node, shenv, true)?,
|
"fg" => continue_job(node, shenv, true)?,
|
||||||
"bg" => continue_job(node, shenv, false)?,
|
"bg" => continue_job(node, shenv, false)?,
|
||||||
"read" => read_builtin(node, shenv)?,
|
"read" => read_builtin(node, shenv)?,
|
||||||
|
"alias" => alias(node, shenv)?,
|
||||||
_ => unimplemented!("Have not yet implemented support for builtin `{}'",command)
|
_ => unimplemented!("Have not yet implemented support for builtin `{}'",command)
|
||||||
}
|
}
|
||||||
log!(TRACE, "done");
|
log!(TRACE, "done");
|
||||||
@@ -212,6 +290,8 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
if let NdRule::Command { argv, redirs: _ } = cmd.rule() {
|
if let NdRule::Command { argv, redirs: _ } = cmd.rule() {
|
||||||
let cmd_name = argv.first().unwrap().span().get_slice().to_string();
|
let cmd_name = argv.first().unwrap().span().get_slice().to_string();
|
||||||
cmd_names.push(cmd_name);
|
cmd_names.push(cmd_name);
|
||||||
|
} else if let NdRule::Subshell {..} = cmd.rule() {
|
||||||
|
cmd_names.push("subshell".to_string());
|
||||||
} else { unimplemented!() }
|
} else { unimplemented!() }
|
||||||
|
|
||||||
match unsafe { fork()? } {
|
match unsafe { fork()? } {
|
||||||
@@ -234,11 +314,7 @@ fn exec_pipeline(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
shenv.ctx_mut().push_rdr(rpipe_redir);
|
shenv.ctx_mut().push_rdr(rpipe_redir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.flags().contains(NdFlag::BUILTIN) {
|
dispatch_command(cmd, shenv)?;
|
||||||
exec_builtin(cmd, shenv).unwrap();
|
|
||||||
} else {
|
|
||||||
exec_cmd(cmd, shenv).unwrap();
|
|
||||||
}
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
Parent { child } => {
|
Parent { child } => {
|
||||||
|
|||||||
75
src/expand/alias.rs
Normal file
75
src/expand/alias.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use crate::{parse::lex::SEPARATORS, prelude::*};
|
||||||
|
|
||||||
|
pub fn expand_aliases(input: &str, shenv: &mut ShEnv) -> Option<String> {
|
||||||
|
let mut result = input.to_string();
|
||||||
|
let mut expanded_aliases = Vec::new();
|
||||||
|
let mut found_in_iteration = true;
|
||||||
|
|
||||||
|
// Loop until no new alias expansion happens.
|
||||||
|
while found_in_iteration {
|
||||||
|
found_in_iteration = false;
|
||||||
|
let mut new_result = String::new();
|
||||||
|
let mut chars = result.chars().peekable();
|
||||||
|
let mut alias_cand = String::new();
|
||||||
|
let mut is_cmd = true;
|
||||||
|
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
match ch {
|
||||||
|
';' | '\n' => {
|
||||||
|
new_result.push(ch);
|
||||||
|
is_cmd = true;
|
||||||
|
// Consume any extra whitespace or delimiters.
|
||||||
|
while let Some(&next_ch) = chars.peek() {
|
||||||
|
if matches!(next_ch, ' ' | '\t' | ';' | '\n') {
|
||||||
|
new_result.push(next_ch);
|
||||||
|
chars.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' ' | '\t' => {
|
||||||
|
is_cmd = false;
|
||||||
|
new_result.push(ch);
|
||||||
|
}
|
||||||
|
_ if is_cmd => {
|
||||||
|
// Accumulate token characters.
|
||||||
|
alias_cand.push(ch);
|
||||||
|
while let Some(&next_ch) = chars.peek() {
|
||||||
|
if matches!(next_ch, ' ' | '\t' | ';' | '\n') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
alias_cand.push(next_ch);
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for an alias expansion.
|
||||||
|
if let Some(alias) = shenv.logic().get_alias(&alias_cand) {
|
||||||
|
// Only expand if we haven't already done so.
|
||||||
|
if !expanded_aliases.contains(&alias_cand) {
|
||||||
|
new_result.push_str(alias);
|
||||||
|
expanded_aliases.push(alias_cand.clone());
|
||||||
|
found_in_iteration = true;
|
||||||
|
} else {
|
||||||
|
new_result.push_str(&alias_cand);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_result.push_str(&alias_cand);
|
||||||
|
}
|
||||||
|
alias_cand.clear();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
new_result.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = new_result;
|
||||||
|
log!(DEBUG, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if expanded_aliases.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,11 @@ pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
|||||||
Lexer::new(value).lex() // Automatically handles word splitting for us
|
Lexer::new(value).lex() // Automatically handles word splitting for us
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> String {
|
pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> Token {
|
||||||
let dquote_raw = dquote.to_string();
|
let dquote_raw = dquote.to_string();
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let mut var_name = String::new();
|
let mut var_name = String::new();
|
||||||
let mut chars = dquote_raw.chars();
|
let mut chars = dquote_raw.chars().peekable();
|
||||||
let mut in_brace = false;
|
let mut in_brace = false;
|
||||||
|
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
@@ -25,11 +25,13 @@ pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> String {
|
|||||||
result.push(next_ch)
|
result.push(next_ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'"' => continue,
|
|
||||||
'$' => {
|
'$' => {
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.peek() {
|
||||||
|
if *ch == '"' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let ch = chars.next().unwrap();
|
||||||
match ch {
|
match ch {
|
||||||
'"' => continue,
|
|
||||||
'{' => {
|
'{' => {
|
||||||
in_brace = true;
|
in_brace = true;
|
||||||
}
|
}
|
||||||
@@ -59,5 +61,7 @@ pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
log!(DEBUG, result);
|
||||||
|
|
||||||
|
Lexer::new(Rc::new(result)).lex().pop().unwrap_or(dquote)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,38 @@
|
|||||||
pub mod expand_vars;
|
pub mod expand_vars;
|
||||||
pub mod tilde;
|
pub mod tilde;
|
||||||
|
pub mod alias;
|
||||||
|
|
||||||
|
use alias::expand_aliases;
|
||||||
|
use expand_vars::{expand_dquote, expand_var};
|
||||||
|
use tilde::expand_tilde;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn expand_argv(argv: Vec<Token>, shenv: &mut ShEnv) -> Vec<Token> {
|
||||||
|
let mut processed = vec![];
|
||||||
|
for arg in argv {
|
||||||
|
log!(DEBUG, arg);
|
||||||
|
log!(DEBUG, processed);
|
||||||
|
match arg.rule() {
|
||||||
|
TkRule::DQuote => {
|
||||||
|
let dquote_exp = expand_dquote(arg.clone(), shenv);
|
||||||
|
processed.push(dquote_exp);
|
||||||
|
}
|
||||||
|
TkRule::VarSub => {
|
||||||
|
let mut varsub_exp = expand_var(arg.clone(), shenv);
|
||||||
|
processed.append(&mut varsub_exp);
|
||||||
|
}
|
||||||
|
TkRule::TildeSub => {
|
||||||
|
let tilde_exp = expand_tilde(arg.clone());
|
||||||
|
processed.push(tilde_exp);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if arg.rule() != TkRule::Ident {
|
||||||
|
log!(WARN, "found this in expand_argv: {:?}", arg.rule());
|
||||||
|
}
|
||||||
|
processed.push(arg.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn expand_tilde(tilde_sub: Token) -> String {
|
pub fn expand_tilde(tilde_sub: Token) -> Token {
|
||||||
let tilde_sub_raw = tilde_sub.to_string();
|
let tilde_sub_raw = tilde_sub.to_string();
|
||||||
if tilde_sub_raw.starts_with('~') {
|
if tilde_sub_raw.starts_with('~') {
|
||||||
let home = std::env::var("HOME").unwrap_or_default();
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
tilde_sub_raw.replacen('~', &home, 1)
|
tilde_sub_raw.replacen('~', &home, 1);
|
||||||
|
let lex_input = Rc::new(tilde_sub_raw);
|
||||||
|
let mut tokens = Lexer::new(lex_input).lex();
|
||||||
|
tokens.pop().unwrap_or(tilde_sub)
|
||||||
} else {
|
} else {
|
||||||
tilde_sub_raw
|
tilde_sub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,14 +89,14 @@ pub enum ShErr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShErr {
|
impl ShErr {
|
||||||
pub fn simple(kind: ShErrKind, message: &str) -> Self {
|
pub fn simple<S: Into<String>>(kind: ShErrKind, message: S) -> Self {
|
||||||
Self::Simple { kind, message: message.to_string() }
|
Self::Simple { kind, message: message.into() }
|
||||||
}
|
}
|
||||||
pub fn io() -> Self {
|
pub fn io() -> Self {
|
||||||
io::Error::last_os_error().into()
|
io::Error::last_os_error().into()
|
||||||
}
|
}
|
||||||
pub fn full(kind: ShErrKind, message: String, span: Span) -> Self {
|
pub fn full<S: Into<String>>(kind: ShErrKind, message: S, span: Span) -> Self {
|
||||||
Self::Full { kind, message, span }
|
Self::Full { kind, message: message.into(), span }
|
||||||
}
|
}
|
||||||
pub fn try_blame(&mut self, blame: Span) {
|
pub fn try_blame(&mut self, blame: Span) {
|
||||||
match self {
|
match self {
|
||||||
@@ -178,7 +178,7 @@ impl Display for ShErr {
|
|||||||
let dist = span.end() - span.start();
|
let dist = span.end() - span.start();
|
||||||
let padding = " ".repeat(offset);
|
let padding = " ".repeat(offset);
|
||||||
let line_inner = "~".repeat(dist.saturating_sub(2));
|
let line_inner = "~".repeat(dist.saturating_sub(2));
|
||||||
let err_kind = style_text(&self.display_kind(), Style::Red | Style::Bold);
|
let err_kind = &self.display_kind().styled(Style::Red | Style::Bold);
|
||||||
let stat_line = format!("[{}:{}] - {}{}",line_no,offset,err_kind,message);
|
let stat_line = format!("[{}:{}] - {}{}",line_no,offset,err_kind,message);
|
||||||
let indicator_line = if dist == 1 {
|
let indicator_line = if dist == 1 {
|
||||||
format!("{}^",padding)
|
format!("{}^",padding)
|
||||||
|
|||||||
@@ -5,8 +5,19 @@ use nix::libc::getpgrp;
|
|||||||
|
|
||||||
use crate::{expand::{expand_vars::{expand_dquote, expand_var}, tilde::expand_tilde}, prelude::*};
|
use crate::{expand::{expand_vars::{expand_dquote, expand_var}, tilde::expand_tilde}, prelude::*};
|
||||||
|
|
||||||
|
use super::term::StyleSet;
|
||||||
|
|
||||||
pub trait StrOps {
|
pub trait StrOps {
|
||||||
fn trim_quotes(&self) -> String;
|
/// This function operates on anything that implements `AsRef<str>` and `Display`, which is mainly strings.
|
||||||
|
/// It takes a 'Style' which can be passed as a single Style object like `Style::Cyan` or a Bit OR of many styles,
|
||||||
|
/// For instance: `Style::Red | Style::Bold | Style::Italic`
|
||||||
|
fn styled<S: Into<StyleSet>>(self, style: S) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<str> + Display> StrOps for T {
|
||||||
|
fn styled<S: Into<StyleSet>>(self, style: S) -> String {
|
||||||
|
style_text(&self, style)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArgVec {
|
pub trait ArgVec {
|
||||||
@@ -16,30 +27,12 @@ pub trait ArgVec {
|
|||||||
|
|
||||||
impl ArgVec for Vec<Token> {
|
impl ArgVec for Vec<Token> {
|
||||||
/// Converts the contained tokens into strings.
|
/// Converts the contained tokens into strings.
|
||||||
/// This function also performs token expansion.
|
|
||||||
fn as_strings(self, shenv: &mut ShEnv) -> Vec<String> {
|
fn as_strings(self, shenv: &mut ShEnv) -> Vec<String> {
|
||||||
let mut argv_iter = self.into_iter();
|
let mut argv_iter = self.into_iter();
|
||||||
let mut argv_processed = vec![];
|
let mut argv_processed = vec![];
|
||||||
while let Some(arg) = argv_iter.next() {
|
while let Some(arg) = argv_iter.next() {
|
||||||
match arg.rule() {
|
let cleaned = trim_quotes(&arg);
|
||||||
TkRule::VarSub => {
|
argv_processed.push(cleaned);
|
||||||
let mut tokens = expand_var(arg, shenv).into_iter();
|
|
||||||
while let Some(token) = tokens.next() {
|
|
||||||
argv_processed.push(token.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TkRule::TildeSub => {
|
|
||||||
let expanded = expand_tilde(arg);
|
|
||||||
argv_processed.push(expanded);
|
|
||||||
}
|
|
||||||
TkRule::DQuote => {
|
|
||||||
let expanded = expand_dquote(arg, shenv);
|
|
||||||
argv_processed.push(expanded)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
argv_processed.push(arg.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
argv_processed
|
argv_processed
|
||||||
}
|
}
|
||||||
@@ -72,11 +65,11 @@ pub enum LogLevel {
|
|||||||
impl Display for LogLevel {
|
impl Display for LogLevel {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ERROR => write!(f,"{}",style_text("ERROR", Style::Red | Style::Bold)),
|
ERROR => write!(f,"{}","ERROR".styled(Style::Red | Style::Bold)),
|
||||||
WARN => write!(f,"{}",style_text("WARN", Style::Yellow | Style::Bold)),
|
WARN => write!(f,"{}","WARN".styled(Style::Yellow | Style::Bold)),
|
||||||
INFO => write!(f,"{}",style_text("INFO", Style::Green | Style::Bold)),
|
INFO => write!(f,"{}","INFO".styled(Style::Green | Style::Bold)),
|
||||||
DEBUG => write!(f,"{}",style_text("DEBUG", Style::Magenta | Style::Bold)),
|
DEBUG => write!(f,"{}","DEBUG".styled(Style::Magenta | Style::Bold)),
|
||||||
TRACE => write!(f,"{}",style_text("TRACE", Style::Blue | Style::Bold)),
|
TRACE => write!(f,"{}","TRACE".styled(Style::Blue | Style::Bold)),
|
||||||
NULL => write!(f,"")
|
NULL => write!(f,"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,9 +82,9 @@ macro_rules! log {
|
|||||||
let var_name = stringify!($var);
|
let var_name = stringify!($var);
|
||||||
if $level <= log_level() {
|
if $level <= log_level() {
|
||||||
let file = file!();
|
let file = file!();
|
||||||
let file_styled = style_text(file,Style::Cyan);
|
let file_styled = file.styled(Style::Cyan);
|
||||||
let line = line!();
|
let line = line!();
|
||||||
let line_styled = style_text(line,Style::Cyan);
|
let line_styled = line.to_string().styled(Style::Cyan);
|
||||||
let logged = format!("[{}][{}:{}] {} = {:#?}",$level, file_styled,line_styled,var_name, &$var);
|
let logged = format!("[{}][{}:{}] {} = {:#?}",$level, file_styled,line_styled,var_name, &$var);
|
||||||
|
|
||||||
write(borrow_fd(2),format!("{}\n",logged).as_bytes()).unwrap();
|
write(borrow_fd(2),format!("{}\n",logged).as_bytes()).unwrap();
|
||||||
@@ -102,9 +95,9 @@ macro_rules! log {
|
|||||||
($level:expr, $lit:literal) => {{
|
($level:expr, $lit:literal) => {{
|
||||||
if $level <= log_level() {
|
if $level <= log_level() {
|
||||||
let file = file!();
|
let file = file!();
|
||||||
let file_styled = style_text(file, Style::Cyan);
|
let file_styled = file.styled(Style::Cyan);
|
||||||
let line = line!();
|
let line = line!();
|
||||||
let line_styled = style_text(line, Style::Cyan);
|
let line_styled = line.to_string().styled(Style::Cyan);
|
||||||
let logged = format!("[{}][{}:{}] {}", $level, file_styled, line_styled, $lit);
|
let logged = format!("[{}][{}:{}] {}", $level, file_styled, line_styled, $lit);
|
||||||
write(borrow_fd(2), format!("{}\n", logged).as_bytes()).unwrap();
|
write(borrow_fd(2), format!("{}\n", logged).as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
@@ -114,9 +107,9 @@ macro_rules! log {
|
|||||||
if $level <= log_level() {
|
if $level <= log_level() {
|
||||||
let formatted = format!($($arg)*);
|
let formatted = format!($($arg)*);
|
||||||
let file = file!();
|
let file = file!();
|
||||||
let file_styled = style_text(file, Style::Cyan);
|
let file_styled = file.styled(Style::Cyan);
|
||||||
let line = line!();
|
let line = line!();
|
||||||
let line_styled = style_text(line, Style::Cyan);
|
let line_styled = line.to_string().styled(Style::Cyan);
|
||||||
let logged = format!("[{}][{}:{}] {}", $level, file_styled, line_styled, formatted);
|
let logged = format!("[{}][{}:{}] {}", $level, file_styled, line_styled, formatted);
|
||||||
write(borrow_fd(2), format!("{}\n", logged).as_bytes()).unwrap();
|
write(borrow_fd(2), format!("{}\n", logged).as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,21 @@ pub fn main() {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
log!(TRACE, "Entered loop");
|
log!(TRACE, "Entered loop");
|
||||||
let line = match prompt::read_line(&mut shenv) {
|
let mut line = match prompt::read_line(&mut shenv) {
|
||||||
Ok(line) => line,
|
Ok(line) => line,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}",e);
|
eprintln!("{}",e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if let Some(line_exp) = expand_aliases(&line, &mut shenv) {
|
||||||
|
line = line_exp;
|
||||||
|
}
|
||||||
let input = Rc::new(line);
|
let input = Rc::new(line);
|
||||||
log!(INFO, "New input: {:?}", input);
|
log!(INFO, "New input: {:?}", input);
|
||||||
let token_stream = Lexer::new(input).lex();
|
let token_stream = Lexer::new(input).lex();
|
||||||
|
log!(DEBUG, token_stream);
|
||||||
|
log!(DEBUG, token_stream);
|
||||||
log!(TRACE, "Token stream: {:?}", token_stream);
|
log!(TRACE, "Token stream: {:?}", token_stream);
|
||||||
match Parser::new(token_stream).parse() {
|
match Parser::new(token_stream).parse() {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ impl Lexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
rule: TkRule,
|
rule: TkRule,
|
||||||
span: Span
|
span: Span
|
||||||
@@ -98,6 +98,13 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Token {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let info = (self.rule(),self.to_string(),self.span.start,self.span.end);
|
||||||
|
write!(f,"{:?}",info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Token {
|
impl Display for Token {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let slice = self.span.get_slice();
|
let slice = self.span.get_slice();
|
||||||
@@ -462,7 +469,7 @@ tkrule_def!(DQuote, |input: &str| {
|
|||||||
// Double quoted strings
|
// Double quoted strings
|
||||||
let mut chars = input.chars();
|
let mut chars = input.chars();
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
let mut quoted = false;
|
let mut quote_count = 0;
|
||||||
|
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
@@ -470,21 +477,38 @@ tkrule_def!(DQuote, |input: &str| {
|
|||||||
chars.next();
|
chars.next();
|
||||||
len += 2;
|
len += 2;
|
||||||
}
|
}
|
||||||
'"' if !quoted => {
|
'"' => {
|
||||||
len += 1;
|
len += 1;
|
||||||
quoted = true;
|
quote_count += 1;
|
||||||
}
|
}
|
||||||
'"' if quoted => {
|
' ' | '\t' | ';' | '\n' if quote_count % 2 == 0 => {
|
||||||
len += 1;
|
if quote_count > 0 {
|
||||||
return Some(len)
|
if quote_count % 2 == 0 {
|
||||||
}
|
return Some(len)
|
||||||
_ if !quoted => {
|
} else {
|
||||||
return None
|
return None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => len += 1
|
_ => len += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
match len {
|
||||||
|
0 => None,
|
||||||
|
_ => {
|
||||||
|
if quote_count > 0 {
|
||||||
|
if quote_count % 2 == 0 {
|
||||||
|
return Some(len)
|
||||||
|
} else {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tkrule_def!(ProcSub, |input: &str| {
|
tkrule_def!(ProcSub, |input: &str| {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ pub enum NdRule {
|
|||||||
Main { cmd_lists: Vec<Node> },
|
Main { cmd_lists: Vec<Node> },
|
||||||
Command { argv: Vec<Token>, redirs: Vec<Redir> },
|
Command { argv: Vec<Token>, redirs: Vec<Redir> },
|
||||||
Assignment { assignments: Vec<Token>, cmd: Option<Box<Node>> },
|
Assignment { assignments: Vec<Token>, cmd: Option<Box<Node>> },
|
||||||
|
FuncDef { name: Token, body: Token },
|
||||||
Subshell { body: Token, argv: Vec<Token>, redirs: Vec<Redir> },
|
Subshell { body: Token, argv: Vec<Token>, redirs: Vec<Redir> },
|
||||||
CmdList { cmds: Vec<(Option<CmdGuard>,Node)> },
|
CmdList { cmds: Vec<(Option<CmdGuard>,Node)> },
|
||||||
Pipeline { cmds: Vec<Node> }
|
Pipeline { cmds: Vec<Node> }
|
||||||
@@ -255,6 +256,7 @@ ndrule_def!(CmdList, |tokens: &[Token]| {
|
|||||||
|
|
||||||
ndrule_def!(Expr, |tokens: &[Token]| {
|
ndrule_def!(Expr, |tokens: &[Token]| {
|
||||||
try_rules!(tokens,
|
try_rules!(tokens,
|
||||||
|
ShellCmd,
|
||||||
Pipeline,
|
Pipeline,
|
||||||
Subshell,
|
Subshell,
|
||||||
Assignment,
|
Assignment,
|
||||||
@@ -264,12 +266,57 @@ ndrule_def!(Expr, |tokens: &[Token]| {
|
|||||||
// Used in pipelines to avoid recursion
|
// Used in pipelines to avoid recursion
|
||||||
ndrule_def!(ExprNoPipeline, |tokens: &[Token]| {
|
ndrule_def!(ExprNoPipeline, |tokens: &[Token]| {
|
||||||
try_rules!(tokens,
|
try_rules!(tokens,
|
||||||
|
ShellCmd,
|
||||||
Subshell,
|
Subshell,
|
||||||
Assignment,
|
Assignment,
|
||||||
Command
|
Command
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ndrule_def!(ShellCmd, |tokens: &[Token]| {
|
||||||
|
try_rules!(tokens,
|
||||||
|
FuncDef
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ndrule_def!(FuncDef, |tokens: &[Token]| {
|
||||||
|
let mut tokens_iter = tokens.iter();
|
||||||
|
let mut node_toks = vec![];
|
||||||
|
let name: Token;
|
||||||
|
let body: Token;
|
||||||
|
|
||||||
|
if let Some(token) = tokens_iter.next() {
|
||||||
|
if let TkRule::FuncName = token.rule() {
|
||||||
|
node_toks.push(token.clone());
|
||||||
|
name = token.clone();
|
||||||
|
} else {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(token) = tokens_iter.next() {
|
||||||
|
if let TkRule::BraceGrp = token.rule() {
|
||||||
|
node_toks.push(token.clone());
|
||||||
|
body = token.clone();
|
||||||
|
} else {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
let span = get_span(&node_toks)?;
|
||||||
|
let node = Node {
|
||||||
|
node_rule: NdRule::FuncDef { name, body },
|
||||||
|
tokens: node_toks,
|
||||||
|
span,
|
||||||
|
flags: NdFlag::empty()
|
||||||
|
};
|
||||||
|
Ok(Some(node))
|
||||||
|
});
|
||||||
|
|
||||||
ndrule_def!(Subshell, |tokens: &[Token]| {
|
ndrule_def!(Subshell, |tokens: &[Token]| {
|
||||||
let mut tokens_iter = tokens.iter();
|
let mut tokens_iter = tokens.iter();
|
||||||
let mut node_toks = vec![];
|
let mut node_toks = vec![];
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ pub use crate::{
|
|||||||
Redir,
|
Redir,
|
||||||
RedirType,
|
RedirType,
|
||||||
RedirBldr,
|
RedirBldr,
|
||||||
|
StrOps,
|
||||||
RedirTarget,
|
RedirTarget,
|
||||||
CmdRedirs,
|
CmdRedirs,
|
||||||
borrow_fd,
|
borrow_fd,
|
||||||
@@ -112,12 +113,17 @@ pub use crate::{
|
|||||||
cd::cd,
|
cd::cd,
|
||||||
pwd::pwd,
|
pwd::pwd,
|
||||||
read::read_builtin,
|
read::read_builtin,
|
||||||
|
alias::alias,
|
||||||
jobctl::{
|
jobctl::{
|
||||||
continue_job,
|
continue_job,
|
||||||
jobs
|
jobs
|
||||||
},
|
},
|
||||||
BUILTINS
|
BUILTINS
|
||||||
},
|
},
|
||||||
|
expand::{
|
||||||
|
expand_argv,
|
||||||
|
alias::expand_aliases
|
||||||
|
},
|
||||||
shellenv::{
|
shellenv::{
|
||||||
self,
|
self,
|
||||||
wait_fg,
|
wait_fg,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ impl<'a> Highlighter for SynHelper<'a> {
|
|||||||
let raw = token.to_string();
|
let raw = token.to_string();
|
||||||
match token.rule() {
|
match token.rule() {
|
||||||
TkRule::Comment => {
|
TkRule::Comment => {
|
||||||
let styled = style_text(&raw, Style::BrightBlack);
|
let styled = &raw.styled(Style::BrightBlack);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
}
|
}
|
||||||
TkRule::ErrPipeOp |
|
TkRule::ErrPipeOp |
|
||||||
@@ -26,46 +26,70 @@ impl<'a> Highlighter for SynHelper<'a> {
|
|||||||
TkRule::RedirOp |
|
TkRule::RedirOp |
|
||||||
TkRule::BgOp => {
|
TkRule::BgOp => {
|
||||||
is_command = true;
|
is_command = true;
|
||||||
let styled = style_text(&raw, Style::Cyan);
|
let styled = &raw.styled(Style::Cyan);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
}
|
}
|
||||||
|
TkRule::FuncName => {
|
||||||
|
let name = raw.strip_suffix("()").unwrap_or(&raw);
|
||||||
|
let styled = name.styled(Style::Cyan);
|
||||||
|
let rebuilt = format!("{styled}()");
|
||||||
|
result.push_str(&rebuilt);
|
||||||
|
}
|
||||||
TkRule::Keyword => {
|
TkRule::Keyword => {
|
||||||
if &raw == "for" {
|
if &raw == "for" {
|
||||||
in_array = true;
|
in_array = true;
|
||||||
}
|
}
|
||||||
let styled = style_text(&raw, Style::Yellow);
|
let styled = &raw.styled(Style::Yellow);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
}
|
}
|
||||||
|
TkRule::BraceGrp => {
|
||||||
|
let body = &raw[1..raw.len() - 1];
|
||||||
|
let highlighted = self.highlight(body, 0).to_string();
|
||||||
|
let styled_o_brace = "{".styled(Style::BrightBlue);
|
||||||
|
let styled_c_brace = "}".styled(Style::BrightBlue);
|
||||||
|
let rebuilt = format!("{styled_o_brace}{highlighted}{styled_c_brace}");
|
||||||
|
|
||||||
|
is_command = false;
|
||||||
|
result.push_str(&rebuilt);
|
||||||
|
}
|
||||||
TkRule::Subshell => {
|
TkRule::Subshell => {
|
||||||
let body = &raw[1..raw.len() - 1];
|
let body = &raw[1..raw.len() - 1];
|
||||||
let highlighted = self.highlight(body, 0).to_string();
|
let highlighted = self.highlight(body, 0).to_string();
|
||||||
let styled_o_paren = style_text("(", Style::BrightBlue);
|
let styled_o_paren = "(".styled(Style::BrightBlue);
|
||||||
let styled_c_paren = style_text(")", Style::BrightBlue);
|
let styled_c_paren = ")".styled(Style::BrightBlue);
|
||||||
let rebuilt = format!("{styled_o_paren}{highlighted}{styled_c_paren}");
|
let rebuilt = format!("{styled_o_paren}{highlighted}{styled_c_paren}");
|
||||||
|
|
||||||
is_command = false;
|
is_command = false;
|
||||||
result.push_str(&rebuilt);
|
result.push_str(&rebuilt);
|
||||||
}
|
}
|
||||||
TkRule::Ident => {
|
TkRule::Ident => {
|
||||||
if in_array {
|
if in_array {
|
||||||
if &raw == "in" {
|
if &raw == "in" {
|
||||||
let styled = style_text(&raw, Style::Yellow);
|
let styled = &raw.styled(Style::Yellow);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
} else {
|
} else {
|
||||||
let styled = style_text(&raw, Style::Magenta);
|
let styled = &raw.styled(Style::Magenta);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if &raw == "{" || &raw == "}" {
|
||||||
|
result.push_str(&raw);
|
||||||
|
|
||||||
} else if is_command {
|
} else if is_command {
|
||||||
if get_bin_path(&token.to_string(), self.shenv).is_some() ||
|
if get_bin_path(&token.to_string(), self.shenv).is_some() ||
|
||||||
self.shenv.logic().get_alias(&raw).is_some() ||
|
self.shenv.logic().get_alias(&raw).is_some() ||
|
||||||
self.shenv.logic().get_function(&raw).is_some() ||
|
self.shenv.logic().get_function(&raw).is_some() ||
|
||||||
BUILTINS.contains(&raw.as_str()) {
|
BUILTINS.contains(&raw.as_str()) {
|
||||||
let styled = style_text(&raw, Style::Green);
|
let styled = &raw.styled(Style::Green);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let styled = style_text(&raw, Style::Red | Style::Bold);
|
let styled = &raw.styled(Style::Red | Style::Bold);
|
||||||
result.push_str(&styled);
|
result.push_str(&styled);
|
||||||
}
|
}
|
||||||
|
|
||||||
is_command = false;
|
is_command = false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result.push_str(&raw);
|
result.push_str(&raw);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use readline::SynHelper;
|
use readline::SynHelper;
|
||||||
use rustyline::{config::Configurer, history::DefaultHistory, ColorMode, CompletionType, Config, DefaultEditor, EditMode, Editor};
|
use rustyline::{config::Configurer, history::{DefaultHistory, History}, ColorMode, CompletionType, Config, DefaultEditor, EditMode, Editor};
|
||||||
|
|
||||||
pub mod readline;
|
pub mod readline;
|
||||||
pub mod highlight;
|
pub mod highlight;
|
||||||
@@ -27,10 +27,19 @@ fn init_rl<'a>(shenv: &'a mut ShEnv) -> Editor<SynHelper<'a>, DefaultHistory> {
|
|||||||
|
|
||||||
pub fn read_line<'a>(shenv: &'a mut ShEnv) -> ShResult<String> {
|
pub fn read_line<'a>(shenv: &'a mut ShEnv) -> ShResult<String> {
|
||||||
log!(TRACE, "Entering prompt");
|
log!(TRACE, "Entering prompt");
|
||||||
let prompt = &style_text("$ ", Style::Green | Style::Bold);
|
let prompt = "$ ".styled(Style::Green | Style::Bold);
|
||||||
let mut editor = init_rl(shenv);
|
let mut editor = init_rl(shenv);
|
||||||
match editor.readline(prompt) {
|
match editor.readline(&prompt) {
|
||||||
Ok(line) => Ok(line),
|
Ok(line) => {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let hist_path = std::env::var("FERN_HIST").ok();
|
||||||
|
editor.history_mut().add(&line).unwrap();
|
||||||
|
if let Some(path) = hist_path {
|
||||||
|
editor.history_mut().save(&PathBuf::from(path)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(line)
|
||||||
|
},
|
||||||
Err(rustyline::error::ReadlineError::Eof) => {
|
Err(rustyline::error::ReadlineError::Eof) => {
|
||||||
kill(Pid::this(), Signal::SIGQUIT)?;
|
kill(Pid::this(), Signal::SIGQUIT)?;
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub struct SynHint {
|
|||||||
|
|
||||||
impl SynHint {
|
impl SynHint {
|
||||||
pub fn new(text: String) -> Self {
|
pub fn new(text: String) -> Self {
|
||||||
let styled = style_text(&text, Style::BrightBlack);
|
let styled = (&text).styled(Style::BrightBlack);
|
||||||
Self { text, styled }
|
Self { text, styled }
|
||||||
}
|
}
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
@@ -78,7 +78,6 @@ impl Hint for SynHint {
|
|||||||
impl<'a> Hinter for SynHelper<'a> {
|
impl<'a> Hinter for SynHelper<'a> {
|
||||||
type Hint = SynHint;
|
type Hint = SynHint;
|
||||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<Self::Hint> {
|
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<Self::Hint> {
|
||||||
return Some(SynHint::empty());
|
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,9 +99,10 @@ impl<'a> ChildProc {
|
|||||||
} else {
|
} else {
|
||||||
WtStat::Exited(pid, 0)
|
WtStat::Exited(pid, 0)
|
||||||
};
|
};
|
||||||
|
log!(DEBUG, command);
|
||||||
let mut child = Self { pgid: pid, pid, command, stat };
|
let mut child = Self { pgid: pid, pid, command, stat };
|
||||||
if let Some(pgid) = pgid {
|
if let Some(pgid) = pgid {
|
||||||
child.set_pgid(pgid)?;
|
child.set_pgid(pgid).ok();
|
||||||
}
|
}
|
||||||
Ok(child)
|
Ok(child)
|
||||||
}
|
}
|
||||||
@@ -345,14 +346,14 @@ impl Job {
|
|||||||
stat_line = format!("{}{} ",pid,stat_line);
|
stat_line = format!("{}{} ",pid,stat_line);
|
||||||
stat_line = format!("{} {}", stat_line, cmd);
|
stat_line = format!("{} {}", stat_line, cmd);
|
||||||
stat_line = match job_stat {
|
stat_line = match job_stat {
|
||||||
WtStat::Stopped(..) | WtStat::Signaled(..) => style_text(stat_line, Style::Magenta),
|
WtStat::Stopped(..) | WtStat::Signaled(..) => stat_line.styled(Style::Magenta),
|
||||||
WtStat::Exited(_, code) => {
|
WtStat::Exited(_, code) => {
|
||||||
match code {
|
match code {
|
||||||
0 => style_text(stat_line, Style::Green),
|
0 => stat_line.styled(Style::Green),
|
||||||
_ => style_text(stat_line, Style::Red),
|
_ => stat_line.styled(Style::Red),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => style_text(stat_line, Style::Cyan)
|
_ => stat_line.styled(Style::Cyan)
|
||||||
};
|
};
|
||||||
if i != self.get_cmds().len() - 1 {
|
if i != self.get_cmds().len() - 1 {
|
||||||
stat_line = format!("{} |",stat_line);
|
stat_line = format!("{} |",stat_line);
|
||||||
|
|||||||
@@ -16,7 +16,13 @@ impl LogTab {
|
|||||||
pub fn get_alias(&self,name: &str) -> Option<&str> {
|
pub fn get_alias(&self,name: &str) -> Option<&str> {
|
||||||
self.aliases.get(name).map(|a| a.as_str())
|
self.aliases.get(name).map(|a| a.as_str())
|
||||||
}
|
}
|
||||||
|
pub fn set_alias(&mut self, name: &str, body: &str) {
|
||||||
|
self.aliases.insert(name.to_string(),body.trim().to_string());
|
||||||
|
}
|
||||||
pub fn get_function(&self,name: &str) -> Option<&str> {
|
pub fn get_function(&self,name: &str) -> Option<&str> {
|
||||||
self.functions.get(name).map(|a| a.as_str())
|
self.functions.get(name).map(|a| a.as_str())
|
||||||
}
|
}
|
||||||
|
pub fn set_function(&mut self, name: &str, body: &str) {
|
||||||
|
self.functions.insert(name.to_string(),body.trim().to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ impl VarTab {
|
|||||||
env::set_var("HOME", home.clone());
|
env::set_var("HOME", home.clone());
|
||||||
env_vars.insert("SHELL".into(), pathbuf_to_string(std::env::current_exe()));
|
env_vars.insert("SHELL".into(), pathbuf_to_string(std::env::current_exe()));
|
||||||
env::set_var("SHELL", pathbuf_to_string(std::env::current_exe()));
|
env::set_var("SHELL", pathbuf_to_string(std::env::current_exe()));
|
||||||
env_vars.insert("HIST_FILE".into(),format!("{}/.fern_hist",home));
|
env_vars.insert("FERN_HIST".into(),format!("{}/.fern_hist",home));
|
||||||
env::set_var("HIST_FILE",format!("{}/.fern_hist",home));
|
env::set_var("FERN_HIST",format!("{}/.fern_hist",home));
|
||||||
|
|
||||||
env_vars
|
env_vars
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user