Skip to content

Commit e3c8320

Browse files
authored
feat: default to reedline and add syntax highlighting support (#187)
Enable reedline as default input backend, with rustyline an alternative option. Also adds an off-by-default syntax highlighting option via the reedline input backend, enabled when passing --enable-highlighting on the brush command line. The highlighting is very much a first cut, with some known issues from a few heuristics and potential perf/latency questions.
1 parent 5baab09 commit e3c8320

9 files changed

Lines changed: 431 additions & 81 deletions

File tree

brush-core/src/expansion.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ impl<'a> WordExpander<'a> {
420420

421421
let mut expansions = vec![];
422422
for piece in pieces {
423-
let piece_expansion = self.expand_word_piece(piece).await?;
423+
let piece_expansion = self.expand_word_piece(piece.piece).await?;
424424
expansions.push(piece_expansion);
425425
}
426426

@@ -540,7 +540,7 @@ impl<'a> WordExpander<'a> {
540540
fields: this_fields,
541541
concatenate,
542542
undefined: _undefined,
543-
} = self.expand_word_piece(piece).await?;
543+
} = self.expand_word_piece(piece.piece).await?;
544544

545545
let fields_to_append = if concatenate {
546546
#[allow(unstable_name_collisions)]
@@ -603,7 +603,8 @@ impl<'a> WordExpander<'a> {
603603
brush_parser::word::WordPiece::ParameterExpansion(p) => {
604604
self.expand_parameter_expr(p).await?
605605
}
606-
brush_parser::word::WordPiece::CommandSubstitution(s) => {
606+
brush_parser::word::WordPiece::BackquotedCommandSubstitution(s)
607+
| brush_parser::word::WordPiece::CommandSubstitution(s) => {
607608
// Insantiate a subshell to run the command in.
608609
let mut subshell = self.shell.clone();
609610

brush-core/src/shell.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ impl Shell {
745745

746746
/// Returns the options that should be used for parsing shell programs; reflects
747747
/// the current configuration state of the shell and may change over time.
748-
pub(crate) fn parser_options(&self) -> brush_parser::ParserOptions {
748+
pub fn parser_options(&self) -> brush_parser::ParserOptions {
749749
brush_parser::ParserOptions {
750750
enable_extended_globbing: self.options.extended_globbing,
751751
posix_mode: self.options.posix_mode,
@@ -880,7 +880,7 @@ impl Shell {
880880
///
881881
/// * `required_glob_pattern` - The glob pattern to match against.
882882
#[allow(clippy::manual_flatten)]
883-
pub(crate) fn find_executables_in_path(&self, required_glob_pattern: &str) -> Vec<PathBuf> {
883+
pub fn find_executables_in_path(&self, required_glob_pattern: &str) -> Vec<PathBuf> {
884884
let is_executable = |path: &Path| path.executable();
885885

886886
let mut executables = vec![];
@@ -1055,6 +1055,19 @@ impl Shell {
10551055
}
10561056
}
10571057

1058+
/// Checks if the given string is a keyword reserved in this shell.
1059+
///
1060+
/// # Arguments
1061+
///
1062+
/// * `s` - The string to check.
1063+
pub fn is_keyword(&self, s: &str) -> bool {
1064+
if self.options.sh_mode {
1065+
keywords::SH_MODE_KEYWORDS.contains(s)
1066+
} else {
1067+
keywords::KEYWORDS.contains(s)
1068+
}
1069+
}
1070+
10581071
/// Checks for completed jobs in the shell, reporting any changes found.
10591072
pub fn check_for_completed_jobs(&mut self) -> Result<(), error::Error> {
10601073
let results = self.jobs.poll()?;
@@ -1078,7 +1091,7 @@ impl Shell {
10781091
}
10791092
}
10801093

1081-
#[cached::proc_macro::cached(size = 32, result = true)]
1094+
#[cached::proc_macro::cached(size = 64, result = true)]
10821095
fn parse_string_impl(
10831096
s: String,
10841097
parser_options: brush_parser::ParserOptions,

brush-interactive/src/reedline/completer.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,30 @@ impl ReedlineCompleter {
2828
completions
2929
.candidates
3030
.into_iter()
31-
.map(|candidate| {
32-
reedline::Suggestion {
33-
value: candidate,
34-
description: None, // TODO: fill in description
35-
style: None, // TODO: fill in style
36-
extra: None, // TODO: fill in extras
37-
span: reedline::Span {
38-
start: insertion_index,
39-
end: insertion_index + delete_count,
40-
},
41-
append_whitespace: false, // TODO: compute this
42-
}
43-
})
31+
.map(|candidate| Self::to_suggestion(candidate, insertion_index, delete_count))
4432
.collect()
4533
}
34+
35+
fn to_suggestion(
36+
candidate: String,
37+
insertion_index: usize,
38+
delete_count: usize,
39+
) -> reedline::Suggestion {
40+
let mut style = nu_ansi_term::Style::new().dimmed();
41+
if candidate.ends_with(std::path::MAIN_SEPARATOR) {
42+
style = style.fg(nu_ansi_term::Color::Green);
43+
}
44+
45+
reedline::Suggestion {
46+
value: candidate,
47+
description: None, // TODO: fill in description
48+
style: Some(style),
49+
extra: None,
50+
span: reedline::Span {
51+
start: insertion_index,
52+
end: insertion_index + delete_count,
53+
},
54+
append_whitespace: false, // TODO: compute this
55+
}
56+
}
4657
}

0 commit comments

Comments
 (0)