Skip to content

Commit c69f6e5

Browse files
casedamikronberger-droid
authored andcommitted
refactor: abbrevation expansion trait logic (nushell#1094)
1 parent 3c9dd4e commit c69f6e5

4 files changed

Lines changed: 49 additions & 29 deletions

File tree

src/engine.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ use {
3636
semantic_prompt::{Osc133ClickEventsMarkers, SemanticPromptMarkers},
3737
},
3838
utils::text_manipulation,
39-
EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, MouseButton,
40-
Prompt, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult,
41-
Validator,
39+
AbbrExpandContext, EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu,
40+
MenuEvent, MouseButton, Prompt, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior,
41+
ValidationResult, Validator,
4242
},
4343
crossterm::{
4444
cursor::{SetCursorStyle, Show},
@@ -626,8 +626,8 @@ impl Reedline {
626626
///
627627
/// Overwrites any existing abbreviations with the same key.
628628
///
629-
/// Note, by default abbreviations are expanded within string literals. To change this behavior
630-
/// override the `is_inside_string_literal` function defined by [`Highlighter`].
629+
/// Note, by default abbreviations are expanded everywhere. To suppress expansion in certain
630+
/// syntactic positions (e.g. string literals), override [`Highlighter::should_expand_abbr`].
631631
pub fn with_abbreviations(mut self, abbreviations: HashMap<String, String>) -> Self {
632632
self.abbreviations.extend(abbreviations);
633633
self
@@ -1758,10 +1758,8 @@ impl Reedline {
17581758

17591759
/// Expands an abbreviation at the word before the cursor, if any exists
17601760
///
1761-
/// Note, this method uses the `is_inside_string_literal` function defined by [`Highlighter`]
1762-
/// to decide whether to expand an abbreviation when the cursor is inside a string literal.
1763-
/// Unless overridden, `is_inside_string_literal` returns `false`, resulting in abbreviations
1764-
/// being expanded even when inside a string literal.
1761+
/// Calls [`Highlighter::should_expand_abbr`] with [`AbbrExpandContext::WordAbbreviation`]
1762+
/// to decide whether expansion is permitted at the cursor position
17651763
fn try_expand_abbreviation_at_cursor(&mut self, submitted: bool) -> Option<ReedlineEvent> {
17661764
let buffer = self.editor.get_buffer();
17671765
let cursor_position_in_buffer = self.editor.insertion_point();
@@ -1787,10 +1785,11 @@ impl Reedline {
17871785
// The first char in the buffer is a space or there are consecutive spaces
17881786
return None;
17891787
}
1790-
if self
1791-
.highlighter
1792-
.is_inside_string_literal(buffer, word_start)
1793-
{
1788+
if !self.highlighter.should_expand_abbr(
1789+
buffer,
1790+
word_start,
1791+
AbbrExpandContext::WordAbbreviation,
1792+
) {
17941793
return None;
17951794
}
17961795

@@ -1826,10 +1825,11 @@ impl Reedline {
18261825
}
18271826
}
18281827

1829-
if self
1830-
.highlighter
1831-
.is_inside_string_literal(buffer, parsed.remainder.len())
1832-
{
1828+
if !self.highlighter.should_expand_abbr(
1829+
buffer,
1830+
parsed.remainder.len(),
1831+
AbbrExpandContext::BangExpansion,
1832+
) {
18331833
return None;
18341834
}
18351835

@@ -2564,7 +2564,7 @@ mod tests {
25642564
set_buffer_at_end(&mut reedline, buffer);
25652565
assert!(
25662566
reedline.try_expand_abbreviation_at_cursor(true).is_some(),
2567-
"must expand when highlighter does not override is_inside_string_literal"
2567+
"must expand when highlighter does not override should_expand_abbr"
25682568
);
25692569
}
25702570

@@ -2648,7 +2648,7 @@ mod tests {
26482648
set_buffer_at_end(&mut reedline, buffer);
26492649
assert!(
26502650
reedline.parse_bang_command().is_some(),
2651-
"must expand when highlighter does not override is_inside_string_literal"
2651+
"must expand when highlighter does not override should_expand_abbr"
26522652
);
26532653
}
26542654
}

src/highlighter/example.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::highlighter::Highlighter;
1+
use crate::highlighter::{AbbrExpandContext, Highlighter};
22
use crate::StyledText;
33
use nu_ansi_term::{Color, Style};
44

@@ -15,9 +15,10 @@ pub struct ExampleHighlighter {
1515
}
1616

1717
impl Highlighter for ExampleHighlighter {
18-
fn is_inside_string_literal(&self, line: &str, cursor: usize) -> bool {
18+
// A simple example of disabling abbreviation expansion within string literals
19+
fn should_expand_abbr(&self, line: &str, cursor: usize, _context: AbbrExpandContext) -> bool {
1920
if line.is_empty() || cursor == 0 {
20-
return false;
21+
return true;
2122
}
2223
let mut in_single = false;
2324
let mut in_double = false;
@@ -40,7 +41,7 @@ impl Highlighter for ExampleHighlighter {
4041
}
4142
byte_pos += 1;
4243
}
43-
in_single || in_double
44+
!(in_single || in_double)
4445
}
4546

4647
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {

src/highlighter/mod.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ use crate::StyledText;
55

66
pub use example::ExampleHighlighter;
77
pub use simple_match::SimpleMatchHighlighter;
8+
9+
/// The context in which abbreviation expansion is being attempted
10+
///
11+
/// Passed to [`Highlighter::should_expand_abbr`] so implementations can apply
12+
/// different veto rules depending on which expansion triggered the check
13+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14+
pub enum AbbrExpandContext {
15+
/// Fish-style word abbreviation
16+
WordAbbreviation,
17+
/// Bashism history expansion
18+
#[cfg(feature = "bashisms")]
19+
BangExpansion,
20+
}
21+
822
/// The syntax highlighting trait. Implementers of this trait will take in the current string and then
923
/// return a `StyledText` object, which represents the contents of the original line as styled strings
1024
pub trait Highlighter: Send {
@@ -13,10 +27,15 @@ pub trait Highlighter: Send {
1327
/// Cursor position as byte offsets in the string
1428
fn highlight(&self, line: &str, cursor: usize) -> StyledText;
1529

16-
/// The action that will take the current buffer and return whether the cursor position (a byte
17-
/// offset) is inside a string literal
18-
fn is_inside_string_literal(&self, line: &str, cursor: usize) -> bool {
19-
let _ = (line, cursor);
20-
false // default for simple highlighters
30+
/// Returns `true` if an abbreviation should be expanded at the given cursor position
31+
/// (a byte offset into `line`), `false` if expansion should be suppressed
32+
///
33+
/// `context` indicates which kind of expansion is being attempted so implementations
34+
/// can apply different veto rules per site
35+
///
36+
/// The default implementation always returns `true` (always expand)
37+
fn should_expand_abbr(&self, line: &str, cursor: usize, context: AbbrExpandContext) -> bool {
38+
let _ = (line, cursor, context);
39+
true
2140
}
2241
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ pub use edit_mode::{
277277
};
278278

279279
mod highlighter;
280-
pub use highlighter::{ExampleHighlighter, Highlighter, SimpleMatchHighlighter};
280+
pub use highlighter::{AbbrExpandContext, ExampleHighlighter, Highlighter, SimpleMatchHighlighter};
281281

282282
mod completion;
283283
pub use completion::{Completer, DefaultCompleter, Span, Suggestion};

0 commit comments

Comments
 (0)