Various improvements
This commit is contained in:
@@ -14,6 +14,7 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
let mut tokens = Lexer::new(line.to_string(),&mut shenv_clone).lex().into_iter();
|
||||
let mut is_command = true;
|
||||
let mut in_array = false;
|
||||
let mut in_case = false;
|
||||
|
||||
while let Some(token) = tokens.next() {
|
||||
let raw = token.as_raw(&mut shenv_clone);
|
||||
@@ -32,19 +33,45 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
let styled = &raw.styled(Style::Cyan);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
TkRule::CasePat => {
|
||||
let pat = raw.trim_end_matches(')');
|
||||
let len_delta = raw.len().saturating_sub(pat.len());
|
||||
let parens = ")".repeat(len_delta);
|
||||
let styled = pat.styled(Style::Magenta);
|
||||
let rebuilt = format!("{styled}{parens}");
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::FuncName => {
|
||||
let name = raw.strip_suffix("()").unwrap_or(&raw);
|
||||
let styled = name.styled(Style::Cyan);
|
||||
let rebuilt = format!("{styled}()");
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
_ if KEYWORDS.contains(&token.rule()) => {
|
||||
if &raw == "for" {
|
||||
in_array = true;
|
||||
}
|
||||
let styled = &raw.styled(Style::Yellow);
|
||||
TkRule::DQuote | TkRule::SQuote => {
|
||||
let styled = raw.styled(Style::BrightYellow);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
_ if KEYWORDS.contains(&token.rule()) => {
|
||||
if in_array || in_case {
|
||||
if &raw == "in" {
|
||||
let styled = &raw.styled(Style::Yellow);
|
||||
result.push_str(&styled);
|
||||
if in_case { in_case = false };
|
||||
} else {
|
||||
let styled = &raw.styled(Style::Magenta);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
} else {
|
||||
if &raw == "for" {
|
||||
in_array = true;
|
||||
}
|
||||
if &raw == "case" {
|
||||
in_case = true;
|
||||
}
|
||||
let styled = &raw.styled(Style::Yellow);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
}
|
||||
TkRule::BraceGrp => {
|
||||
let body = &raw[1..raw.len() - 1];
|
||||
let highlighted = self.highlight(body, 0).to_string();
|
||||
@@ -65,16 +92,30 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
is_command = false;
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::VarSub => {
|
||||
let styled = raw.styled(Style::Magenta);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
TkRule::Assign => {
|
||||
let (var,val) = raw.split_once('=').unwrap();
|
||||
let var_styled = var.styled(Style::Magenta);
|
||||
let val_styled = val.styled(Style::Cyan);
|
||||
let rebuilt = vec![var_styled,val_styled].join("=");
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::Ident => {
|
||||
if in_array {
|
||||
if in_array || in_case {
|
||||
if &raw == "in" {
|
||||
let styled = &raw.styled(Style::Yellow);
|
||||
result.push_str(&styled);
|
||||
if in_case { in_case = false };
|
||||
} else {
|
||||
let styled = &raw.styled(Style::Magenta);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
|
||||
} else if raw.starts_with(['"','\'']) {
|
||||
let styled = &raw.styled(Style::BrightYellow);
|
||||
result.push_str(&styled);
|
||||
} else if &raw == "{" || &raw == "}" {
|
||||
result.push_str(&raw);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ impl<'a> Hinter for SynHelper<'a> {
|
||||
}
|
||||
let history = ctx.history();
|
||||
let result = self.hist_search(line, history)?;
|
||||
let window = result[line.len()..].to_string();
|
||||
let window = result[line.len()..].trim_end().to_string();
|
||||
Some(SynHint::new(window))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,26 @@ use super::readline::SynHelper;
|
||||
pub fn check_delims(line: &str) -> bool {
|
||||
let mut delim_stack = vec![];
|
||||
let mut chars = line.chars();
|
||||
let mut in_case = false;
|
||||
let mut case_depth: u64 = 0;
|
||||
let mut case_check = String::new();
|
||||
let mut in_quote = None; // Tracks which quote type is open (`'` or `"`)
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
case_check.push(ch);
|
||||
if case_check.len() > 4 {
|
||||
case_check = case_check[1..].to_string();
|
||||
}
|
||||
if case_check.ends_with("case") {
|
||||
in_case = true;
|
||||
case_depth += 1;
|
||||
}
|
||||
if case_check.ends_with("esac") {
|
||||
in_case = false;
|
||||
case_depth = case_depth.saturating_sub(1);
|
||||
}
|
||||
match ch {
|
||||
'{' | '(' | '[' if in_quote.is_none() => delim_stack.push(ch),
|
||||
'}' if in_quote.is_none() && delim_stack.pop() != Some('{') => return false,
|
||||
')' if in_quote.is_none() && delim_stack.pop() != Some('(') => {
|
||||
if !in_case {
|
||||
if case_depth == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -44,75 +47,9 @@ pub fn check_delims(line: &str) -> bool {
|
||||
}
|
||||
|
||||
pub fn check_keywords(line: &str, shenv: &mut ShEnv) -> bool {
|
||||
use TkRule::*;
|
||||
let mut expecting: Vec<Vec<TkRule>> = vec![];
|
||||
let mut tokens = Lexer::new(line.to_string(),shenv).lex().into_iter();
|
||||
|
||||
while let Some(token) = tokens.next() {
|
||||
match token.rule() {
|
||||
If => {
|
||||
expecting.push(vec![Then]);
|
||||
}
|
||||
Then => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Then) {
|
||||
expecting.push(vec![Elif, Else, Fi])
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
Elif => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Elif) {
|
||||
expecting.push(vec![Then])
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
Else => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Else) {
|
||||
expecting.push(vec![Fi])
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
Fi => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Fi) {
|
||||
/* Do nothing */
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
While | Until | For | Select => {
|
||||
expecting.push(vec![Do])
|
||||
}
|
||||
Do => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Do) {
|
||||
expecting.push(vec![Done])
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
Done => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Done) {
|
||||
/* Do nothing */
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
Case => {
|
||||
expecting.push(vec![Esac])
|
||||
}
|
||||
Esac => {
|
||||
if let Some(frame) = expecting.pop() {
|
||||
if frame.contains(&Esac) {
|
||||
/* Do nothing */
|
||||
} else { return false }
|
||||
} else { return false }
|
||||
}
|
||||
_ => { /* Do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
expecting.is_empty()
|
||||
shenv.new_input(line);
|
||||
let tokens = Lexer::new(line.to_string(),shenv).lex();
|
||||
Parser::new(tokens, shenv).parse().is_ok()
|
||||
}
|
||||
|
||||
impl<'a> Validator for SynHelper<'a> {
|
||||
|
||||
Reference in New Issue
Block a user