Add array support for local/export/readonly builtins
Add array length syntax ${arr[#]}
Map read path now expands variables before splitting on ., fixing map "$node" with dotted paths
Map assignment path uses quote-aware token splitting, enabling quoted keys like "--type="
Completion errors now display above prompt instead of being overwritten
Fix nested if/fi parser bug when closing keywords appear on separate lines
Add QuoteState enum, replacing ad-hoc quote tracking booleans across lexer, highlighter, and expansion
Add split_tk_at/split_tk for quote-aware token splitting with span preservation
Refactor setup_builtin to accept optional argv for deferred expansion
Add ariadne dependency (not yet wired up)
This commit is contained in:
@@ -8,7 +8,7 @@ use vimode::{CmdReplay, ModeReport, ViInsert, ViMode, ViNormal, ViReplace, ViVis
|
||||
|
||||
use crate::expand::expand_prompt;
|
||||
use crate::libsh::sys::TTY_FILENO;
|
||||
use crate::parse::lex::LexStream;
|
||||
use crate::parse::lex::{LexStream, QuoteState};
|
||||
use crate::prelude::*;
|
||||
use crate::readline::term::{Pos, calc_str_width};
|
||||
use crate::state::read_shopts;
|
||||
@@ -339,8 +339,14 @@ impl ShedVi {
|
||||
let line = self.editor.as_str().to_string();
|
||||
let cursor_pos = self.editor.cursor_byte_pos();
|
||||
|
||||
match self.completer.complete(line, cursor_pos, direction)? {
|
||||
Some(line) => {
|
||||
match self.completer.complete(line, cursor_pos, direction) {
|
||||
Err(e) => {
|
||||
self.writer.flush_write(&format!("\n{e}\n\n"))?;
|
||||
|
||||
// Printing the error invalidates the layout
|
||||
self.old_layout = None;
|
||||
}
|
||||
Ok(Some(line)) => {
|
||||
let span_start = self.completer.token_span.0;
|
||||
let new_cursor = span_start
|
||||
+ self
|
||||
@@ -361,7 +367,7 @@ impl ShedVi {
|
||||
let hint = self.history.get_hint();
|
||||
self.editor.set_hint(hint);
|
||||
}
|
||||
None => {
|
||||
Ok(None) => {
|
||||
self.writer.send_bell().ok();
|
||||
}
|
||||
}
|
||||
@@ -1005,8 +1011,7 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
|
||||
|
||||
let span_start = token.span.start;
|
||||
|
||||
let mut in_dub_qt = false;
|
||||
let mut in_sng_qt = false;
|
||||
let mut qt_state = QuoteState::default();
|
||||
let mut cmd_sub_depth = 0;
|
||||
let mut proc_sub_depth = 0;
|
||||
|
||||
@@ -1045,7 +1050,7 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
|
||||
}
|
||||
}
|
||||
}
|
||||
'$' if !in_sng_qt => {
|
||||
'$' if !qt_state.in_single() => {
|
||||
let dollar_pos = index;
|
||||
token_chars.next(); // consume the dollar
|
||||
if let Some((_, dollar_ch)) = token_chars.peek() {
|
||||
@@ -1115,13 +1120,13 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
|
||||
token_chars.next(); // consume the char with no special handling
|
||||
}
|
||||
|
||||
'\\' if !in_sng_qt => {
|
||||
'\\' if !qt_state.in_single() => {
|
||||
token_chars.next(); // consume the backslash
|
||||
if token_chars.peek().is_some() {
|
||||
token_chars.next(); // consume the escaped char
|
||||
}
|
||||
}
|
||||
'<' | '>' if !in_dub_qt && !in_sng_qt && cmd_sub_depth == 0 && proc_sub_depth == 0 => {
|
||||
'<' | '>' if !qt_state.in_quote() && cmd_sub_depth == 0 && proc_sub_depth == 0 => {
|
||||
token_chars.next();
|
||||
if let Some((_, proc_sub_ch)) = token_chars.peek()
|
||||
&& *proc_sub_ch == '('
|
||||
@@ -1133,25 +1138,25 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
|
||||
}
|
||||
}
|
||||
}
|
||||
'"' if !in_sng_qt => {
|
||||
if in_dub_qt {
|
||||
'"' if !qt_state.in_single() => {
|
||||
if qt_state.in_double() {
|
||||
insertions.push((span_start + *i + 1, markers::STRING_DQ_END));
|
||||
} else {
|
||||
insertions.push((span_start + *i, markers::STRING_DQ));
|
||||
}
|
||||
in_dub_qt = !in_dub_qt;
|
||||
qt_state.toggle_double();
|
||||
token_chars.next(); // consume the quote
|
||||
}
|
||||
'\'' if !in_dub_qt => {
|
||||
if in_sng_qt {
|
||||
'\'' if !qt_state.in_double() => {
|
||||
if qt_state.in_single() {
|
||||
insertions.push((span_start + *i + 1, markers::STRING_SQ_END));
|
||||
} else {
|
||||
insertions.push((span_start + *i, markers::STRING_SQ));
|
||||
}
|
||||
in_sng_qt = !in_sng_qt;
|
||||
qt_state.toggle_single();
|
||||
token_chars.next(); // consume the quote
|
||||
}
|
||||
'[' if !in_dub_qt && !in_sng_qt && !token.flags.contains(TkFlags::ASSIGN) => {
|
||||
'[' if !qt_state.in_quote() && !token.flags.contains(TkFlags::ASSIGN) => {
|
||||
token_chars.next(); // consume the opening bracket
|
||||
let start_pos = span_start + index;
|
||||
let mut is_glob_pat = false;
|
||||
@@ -1177,7 +1182,7 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
|
||||
insertions.push((start_pos, markers::GLOB));
|
||||
}
|
||||
}
|
||||
'*' | '?' if (!in_dub_qt && !in_sng_qt) => {
|
||||
'*' | '?' if !qt_state.in_quote() => {
|
||||
let glob_ch = *ch;
|
||||
token_chars.next(); // consume the first glob char
|
||||
if !in_context(markers::COMMAND, &insertions) {
|
||||
|
||||
Reference in New Issue
Block a user