Some highlighter bug fixes

This commit is contained in:
2026-02-18 11:29:16 -05:00
parent 43b171fab1
commit af70266f6a
3 changed files with 82 additions and 18 deletions

View File

@@ -191,6 +191,7 @@ fn fern_interactive() -> ShResult<()> {
// Process any available input // Process any available input
match readline.process_input() { match readline.process_input() {
Ok(ReadlineEvent::Line(input)) => { Ok(ReadlineEvent::Line(input)) => {
let start = Instant::now();
write_meta(|m| m.start_timer()); write_meta(|m| m.start_timer());
if let Err(e) = exec_input(input, None, true) { if let Err(e) = exec_input(input, None, true) {
match e.kind() { match e.kind() {
@@ -201,10 +202,14 @@ fn fern_interactive() -> ShResult<()> {
_ => eprintln!("{e}"), _ => eprintln!("{e}"),
} }
} }
let command_run_time = start.elapsed();
write_meta(|m| m.stop_timer()); write_meta(|m| m.stop_timer());
// Reset for next command with fresh prompt // Reset for next command with fresh prompt
readline.reset(get_prompt().ok()); readline.reset(get_prompt().ok());
let real_end = start.elapsed();
log::info!("Command execution time: {:.2?}", command_run_time);
log::info!("Total round trip time: {:.2?}", real_end);
} }
Ok(ReadlineEvent::Eof) => { Ok(ReadlineEvent::Eof) => {
// Ctrl+D on empty line // Ctrl+D on empty line

View File

@@ -1,4 +1,3 @@
pub mod highlight;
pub mod readline; pub mod readline;
pub mod statusline; pub mod statusline;

View File

@@ -52,6 +52,16 @@ pub mod markers {
pub const GLOB: char = '\u{fdde}'; pub const GLOB: char = '\u{fdde}';
pub const RESET: char = '\u{fde2}'; pub const RESET: char = '\u{fde2}';
pub const END_MARKERS: [char;7] = [
VAR_SUB_END,
CMD_SUB_END,
PROC_SUB_END,
STRING_DQ_END,
STRING_SQ_END,
SUBSH_END,
RESET
];
} }
/// Non-blocking readline result /// Non-blocking readline result
@@ -161,6 +171,8 @@ impl FernVi {
} }
if cmd.should_submit() { if cmd.should_submit() {
self.editor.set_hint(None);
self.print_line()?;
self.writer.flush_write("\n")?; self.writer.flush_write("\n")?;
let buf = self.editor.take_buf(); let buf = self.editor.take_buf();
// Save command to history // Save command to history
@@ -512,6 +524,63 @@ pub fn marker_for(class: &TkRule) -> Option<char> {
} }
pub fn annotate_token(input: &mut String, token: Tk) { pub fn annotate_token(input: &mut String, token: Tk) {
let sort_insertions = |insertions: &mut Vec<(usize, char)>| {
insertions.sort_by(|a, b| {
match b.0.cmp(&a.0) {
std::cmp::Ordering::Equal => {
let priority = |m: char| -> u8 {
match m {
markers::RESET => 0,
markers::VAR_SUB_END |
markers::CMD_SUB_END |
markers::PROC_SUB_END |
markers::STRING_DQ_END |
markers::STRING_SQ_END |
markers::SUBSH_END => 2,
_ => 1,
}
};
priority(a.1).cmp(&priority(b.1))
}
other => other,
}
});
};
let in_context = |c: char, insertions: &[(usize, char)]| -> bool {
let mut stack = insertions.to_vec();
stack.sort_by(|a, b| {
match b.0.cmp(&a.0) {
std::cmp::Ordering::Equal => {
let priority = |m: char| -> u8 {
match m {
markers::RESET => 0,
markers::VAR_SUB_END |
markers::CMD_SUB_END |
markers::PROC_SUB_END |
markers::STRING_DQ_END |
markers::STRING_SQ_END |
markers::SUBSH_END => 2,
_ => 1,
}
};
priority(a.1).cmp(&priority(b.1))
}
other => other,
}
});
stack.retain(|(i, m)| *i <= token.span.start && !markers::END_MARKERS.contains(m));
log::error!("Checking context for token '{}', looking for '{}'", token.span.as_str(), c);
let Some(ctx) = stack.last() else {
return false;
};
log::error!("Context stack for token '{}': {:?}", token.span.as_str(), stack);
log::error!("Found context marker '{}' at position {}", ctx.1, ctx.0);
ctx.1 == c
};
if token.class != TkRule::Str if token.class != TkRule::Str
&& let Some(marker) = marker_for(&token.class) { && let Some(marker) = marker_for(&token.class) {
input.insert(token.span.end, markers::RESET); input.insert(token.span.end, markers::RESET);
@@ -545,6 +614,8 @@ pub fn annotate_token(input: &mut String, token: Tk) {
insertions.insert(0, (span_start, markers::BUILTIN)); insertions.insert(0, (span_start, markers::BUILTIN));
} else if token.flags.contains(TkFlags::IS_CMD) { } else if token.flags.contains(TkFlags::IS_CMD) {
insertions.insert(0, (span_start, markers::COMMAND)); insertions.insert(0, (span_start, markers::COMMAND));
} else if !token.flags.contains(TkFlags::KEYWORD) && !token.flags.contains(TkFlags::ASSIGN) {
insertions.insert(0, (span_start, markers::ARG));
} }
if token.flags.contains(TkFlags::KEYWORD) { if token.flags.contains(TkFlags::KEYWORD) {
@@ -590,7 +661,7 @@ pub fn annotate_token(input: &mut String, token: Tk) {
'{' if cmd_sub_depth == 0 => { '{' if cmd_sub_depth == 0 => {
insertions.push((span_start + dollar_pos, markers::VAR_SUB)); insertions.push((span_start + dollar_pos, markers::VAR_SUB));
token_chars.next(); // consume the brace token_chars.next(); // consume the brace
let mut end_pos = dollar_pos + 2; // position after ${ let mut end_pos; // position after ${
while let Some((cur_i, br_ch)) = token_chars.peek() { while let Some((cur_i, br_ch)) = token_chars.peek() {
end_pos = *cur_i; end_pos = *cur_i;
// TODO: implement better parameter expansion awareness here // TODO: implement better parameter expansion awareness here
@@ -705,7 +776,10 @@ pub fn annotate_token(input: &mut String, token: Tk) {
} }
} }
'*' | '?' if (!in_dub_qt && !in_sng_qt) => { '*' | '?' if (!in_dub_qt && !in_sng_qt) => {
if !in_context(markers::COMMAND, &insertions) {
insertions.push((span_start + *i + 1, markers::RESET));
insertions.push((span_start + *i, markers::GLOB)); insertions.push((span_start + *i, markers::GLOB));
}
token_chars.next(); // consume the glob char token_chars.next(); // consume the glob char
} }
_ => { _ => {
@@ -719,21 +793,7 @@ pub fn annotate_token(input: &mut String, token: Tk) {
// - Regular markers middle // - Regular markers middle
// - END markers last (inserted last, ends up leftmost) // - END markers last (inserted last, ends up leftmost)
// Result: [END][TOGGLE][RESET] // Result: [END][TOGGLE][RESET]
insertions.sort_by(|a, b| { sort_insertions(&mut insertions);
match b.0.cmp(&a.0) {
std::cmp::Ordering::Equal => {
let priority = |m: char| -> u8 {
match m {
markers::RESET => 0,
markers::VAR_SUB_END | markers::CMD_SUB_END => 2,
_ => 1,
}
};
priority(a.1).cmp(&priority(b.1))
}
other => other,
}
});
for (pos, marker) in insertions { for (pos, marker) in insertions {
let pos = pos.max(0).min(input.len()); let pos = pos.max(0).min(input.len());