mirror of
https://github.com/nushell/nushell.git
synced 2024-12-23 15:39:06 +01:00
Highlight matching brackets / parentheses (#6655)
* [4325] - wip * [4325] - hightlight only matched symbol * [4325] - cleanup * [4325] - match bracket while typing * [4325] - fix clippy * [4325] - add bracket highlight configuration * [4325] - fix working with non-ascii
This commit is contained in:
parent
fe7e87ee02
commit
8224ec49bc
@ -1,9 +1,10 @@
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::get_shape_color;
|
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
|
use nu_protocol::ast::{Argument, Block, Expr, Expression};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use nu_protocol::Config;
|
use nu_protocol::{Config, Span};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
@ -15,10 +16,12 @@ impl Highlighter for NuHighlighter {
|
|||||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
let (shapes, global_span_offset) = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
|
let block = {
|
||||||
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||||
|
block
|
||||||
|
};
|
||||||
|
let (shapes, global_span_offset) = {
|
||||||
let shapes = flatten_block(&working_set, &block);
|
let shapes = flatten_block(&working_set, &block);
|
||||||
(shapes, self.engine_state.next_span_start())
|
(shapes, self.engine_state.next_span_start())
|
||||||
};
|
};
|
||||||
@ -26,6 +29,15 @@ impl Highlighter for NuHighlighter {
|
|||||||
let mut output = StyledText::default();
|
let mut output = StyledText::default();
|
||||||
let mut last_seen_span = global_span_offset;
|
let mut last_seen_span = global_span_offset;
|
||||||
|
|
||||||
|
let global_cursor_offset = _cursor + global_span_offset;
|
||||||
|
let matching_brackets_pos = find_matching_brackets(
|
||||||
|
line,
|
||||||
|
&working_set,
|
||||||
|
&block,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
);
|
||||||
|
|
||||||
for shape in &shapes {
|
for shape in &shapes {
|
||||||
if shape.0.end <= last_seen_span
|
if shape.0.end <= last_seen_span
|
||||||
|| last_seen_span < global_span_offset
|
|| last_seen_span < global_span_offset
|
||||||
@ -44,166 +56,71 @@ impl Highlighter for NuHighlighter {
|
|||||||
let next_token = line
|
let next_token = line
|
||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
|
macro_rules! add_colored_token_with_bracket_highlight {
|
||||||
|
($shape:expr, $span:expr, $text:expr) => {{
|
||||||
|
let spans = split_span_by_highlight_positions(
|
||||||
|
line,
|
||||||
|
&$span,
|
||||||
|
&matching_brackets_pos,
|
||||||
|
global_span_offset,
|
||||||
|
);
|
||||||
|
spans.iter().for_each(|(part, highlight)| {
|
||||||
|
let start = part.start - $span.start;
|
||||||
|
let end = part.end - $span.start;
|
||||||
|
let text = (&next_token[start..end]).to_string();
|
||||||
|
let mut style = get_shape_color($shape.to_string(), &self.config);
|
||||||
|
if *highlight {
|
||||||
|
style = get_matching_brackets_style(style, &self.config);
|
||||||
|
}
|
||||||
|
output.push((style, text));
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! add_colored_token {
|
||||||
|
($shape:expr, $text:expr) => {
|
||||||
|
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
FlatShape::Garbage => output.push((
|
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Garbage
|
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Nothing => output.push((
|
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Nothing
|
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::External => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Binary => {
|
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
||||||
// nushell ?
|
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
||||||
output.push((
|
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::String => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
||||||
))
|
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
||||||
}
|
|
||||||
FlatShape::Bool => {
|
|
||||||
// nushell ?
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Int => {
|
|
||||||
// nushell Int
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Float => {
|
|
||||||
// nushell Decimal
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Range => output.push((
|
|
||||||
// nushell DotDot ?
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::InternalCall => output.push((
|
|
||||||
// nushell InternalCommand
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::External => {
|
|
||||||
// nushell ExternalCommand
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::ExternalArg => {
|
|
||||||
// nushell ExternalWord
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Literal => {
|
|
||||||
// nushell ?
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Operator => output.push((
|
|
||||||
// nushell Operator
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Signature => output.push((
|
|
||||||
// nushell ?
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::String => {
|
|
||||||
// nushell String
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::StringInterpolation => {
|
|
||||||
// nushell ???
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::DateTime => {
|
|
||||||
// nushell ???
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::List => {
|
FlatShape::List => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Table => {
|
FlatShape::Table => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Record => {
|
FlatShape::Record => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Block => {
|
FlatShape::Block => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Filepath => output.push((
|
|
||||||
// nushell Path
|
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Directory => output.push((
|
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Directory
|
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::GlobPattern => output.push((
|
|
||||||
// nushell GlobPattern
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Variable => output.push((
|
|
||||||
// nushell Variable
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Flag => {
|
|
||||||
// nushell Flag
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Custom(..) => output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
last_seen_span = shape.0.end;
|
||||||
}
|
}
|
||||||
@ -216,3 +133,296 @@ impl Highlighter for NuHighlighter {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_span_by_highlight_positions(
|
||||||
|
line: &str,
|
||||||
|
span: &Span,
|
||||||
|
highlight_positions: &Vec<usize>,
|
||||||
|
global_span_offset: usize,
|
||||||
|
) -> Vec<(Span, bool)> {
|
||||||
|
let mut start = span.start;
|
||||||
|
let mut result: Vec<(Span, bool)> = Vec::new();
|
||||||
|
for pos in highlight_positions {
|
||||||
|
if start <= *pos && pos < &span.end {
|
||||||
|
if start < *pos {
|
||||||
|
result.push((Span { start, end: *pos }, false));
|
||||||
|
}
|
||||||
|
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
||||||
|
let end = span_str
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.map(|c| pos + get_char_length(c))
|
||||||
|
.unwrap_or(pos + 1);
|
||||||
|
result.push((Span { start: *pos, end }, true));
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start < span.end {
|
||||||
|
result.push((
|
||||||
|
Span {
|
||||||
|
start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_brackets(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
block: &Block,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Vec<usize> {
|
||||||
|
const BRACKETS: &str = "{}[]()";
|
||||||
|
|
||||||
|
// calculate first bracket position
|
||||||
|
let global_end_offset = line.len() + global_span_offset;
|
||||||
|
let global_bracket_pos =
|
||||||
|
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
|
||||||
|
// cursor is at the end of a non-empty string -- find block end at the previous position
|
||||||
|
if let Some(last_char) = line.chars().last() {
|
||||||
|
global_cursor_offset - get_char_length(last_char)
|
||||||
|
} else {
|
||||||
|
global_cursor_offset
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cursor is in the middle of a string -- find block end at the current position
|
||||||
|
global_cursor_offset
|
||||||
|
};
|
||||||
|
|
||||||
|
// check that position contains bracket
|
||||||
|
let match_idx = global_bracket_pos - global_span_offset;
|
||||||
|
if match_idx >= line.len()
|
||||||
|
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
|
||||||
|
{
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find matching bracket by finding matching block end
|
||||||
|
let matching_block_end = find_matching_block_end_in_block(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
block,
|
||||||
|
global_span_offset,
|
||||||
|
global_bracket_pos,
|
||||||
|
);
|
||||||
|
if let Some(pos) = matching_block_end {
|
||||||
|
let matching_idx = pos - global_span_offset;
|
||||||
|
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
|
||||||
|
return if global_bracket_pos < pos {
|
||||||
|
vec![global_bracket_pos, pos]
|
||||||
|
} else {
|
||||||
|
vec![pos, global_bracket_pos]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_block_end_in_block(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
block: &Block,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
for p in &block.pipelines {
|
||||||
|
for e in &p.expressions {
|
||||||
|
if e.span.contains(global_cursor_offset) {
|
||||||
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
e,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
) {
|
||||||
|
return Some(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_block_end_in_expr(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
expression: &Expression,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
macro_rules! find_in_expr_or_continue {
|
||||||
|
($inner_expr:ident) => {
|
||||||
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
$inner_expr,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
) {
|
||||||
|
return Some(pos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if expression.span.contains(global_cursor_offset) {
|
||||||
|
let expr_first = expression.span.start;
|
||||||
|
let span_str = &line
|
||||||
|
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
|
||||||
|
let expr_last = span_str
|
||||||
|
.chars()
|
||||||
|
.last()
|
||||||
|
.map(|c| expression.span.end - get_char_length(c))
|
||||||
|
.unwrap_or(expression.span.start);
|
||||||
|
|
||||||
|
return match &expression.expr {
|
||||||
|
Expr::Bool(_) => None,
|
||||||
|
Expr::Int(_) => None,
|
||||||
|
Expr::Float(_) => None,
|
||||||
|
Expr::Binary(_) => None,
|
||||||
|
Expr::Range(..) => None,
|
||||||
|
Expr::Var(_) => None,
|
||||||
|
Expr::VarDecl(_) => None,
|
||||||
|
Expr::ExternalCall(..) => None,
|
||||||
|
Expr::Operator(_) => None,
|
||||||
|
Expr::UnaryNot(_) => None,
|
||||||
|
Expr::Keyword(..) => None,
|
||||||
|
Expr::ValueWithUnit(..) => None,
|
||||||
|
Expr::DateTime(_) => None,
|
||||||
|
Expr::Filepath(_) => None,
|
||||||
|
Expr::Directory(_) => None,
|
||||||
|
Expr::GlobPattern(_) => None,
|
||||||
|
Expr::String(_) => None,
|
||||||
|
Expr::CellPath(_) => None,
|
||||||
|
Expr::ImportPattern(_) => None,
|
||||||
|
Expr::Overlay(_) => None,
|
||||||
|
Expr::Signature(_) => None,
|
||||||
|
Expr::Nothing => None,
|
||||||
|
Expr::Garbage => None,
|
||||||
|
|
||||||
|
Expr::Table(hdr, rows) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at table end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at table start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside table
|
||||||
|
for inner_expr in hdr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
for row in rows {
|
||||||
|
for inner_expr in row {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Record(exprs) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at record end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at record start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside record
|
||||||
|
for (k, v) in exprs {
|
||||||
|
find_in_expr_or_continue!(k);
|
||||||
|
find_in_expr_or_continue!(v);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Call(call) => {
|
||||||
|
for arg in &call.arguments {
|
||||||
|
let opt_expr = match arg {
|
||||||
|
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
||||||
|
Argument::Positional(inner_expr) => Some(inner_expr),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(inner_expr) = opt_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
&b.head,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
),
|
||||||
|
|
||||||
|
Expr::BinaryOp(lhs, op, rhs) => {
|
||||||
|
find_in_expr_or_continue!(lhs);
|
||||||
|
find_in_expr_or_continue!(op);
|
||||||
|
find_in_expr_or_continue!(rhs);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Block(block_id)
|
||||||
|
| Expr::RowCondition(block_id)
|
||||||
|
| Expr::Subexpression(block_id) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at block end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at block start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside block
|
||||||
|
let nested_block = working_set.get_block(*block_id);
|
||||||
|
find_matching_block_end_in_block(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
nested_block,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::StringInterpolation(inner_expr) => {
|
||||||
|
for inner_expr in inner_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::List(inner_expr) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at list end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at list start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside list
|
||||||
|
for inner_expr in inner_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
|
||||||
|
s[index..].chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_length(c: char) -> usize {
|
||||||
|
c.to_string().len()
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
mod color_config;
|
mod color_config;
|
||||||
|
mod matching_brackets_style;
|
||||||
mod nu_style;
|
mod nu_style;
|
||||||
mod shape_color;
|
mod shape_color;
|
||||||
|
|
||||||
pub use color_config::*;
|
pub use color_config::*;
|
||||||
|
pub use matching_brackets_style::*;
|
||||||
pub use nu_style::*;
|
pub use nu_style::*;
|
||||||
pub use shape_color::*;
|
pub use shape_color::*;
|
||||||
|
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::color_config::lookup_ansi_color_style;
|
||||||
|
use nu_ansi_term::Style;
|
||||||
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
|
||||||
|
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
|
||||||
|
|
||||||
|
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
|
||||||
|
Some(int_color) => match int_color.as_string() {
|
||||||
|
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
|
||||||
|
Err(_) => default_style,
|
||||||
|
},
|
||||||
|
None => default_style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_styles(base: Style, extra: Style) -> Style {
|
||||||
|
Style {
|
||||||
|
foreground: extra.foreground.or(base.foreground),
|
||||||
|
background: extra.background.or(base.background),
|
||||||
|
is_bold: extra.is_bold || base.is_bold,
|
||||||
|
is_dimmed: extra.is_dimmed || base.is_dimmed,
|
||||||
|
is_italic: extra.is_italic || base.is_italic,
|
||||||
|
is_underline: extra.is_underline || base.is_underline,
|
||||||
|
is_blink: extra.is_blink || base.is_blink,
|
||||||
|
is_reverse: extra.is_reverse || base.is_reverse,
|
||||||
|
is_hidden: extra.is_hidden || base.is_hidden,
|
||||||
|
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
|
||||||
|
}
|
||||||
|
}
|
@ -179,6 +179,7 @@ let dark_theme = {
|
|||||||
shape_flag: blue_bold
|
shape_flag: blue_bold
|
||||||
shape_custom: green
|
shape_custom: green
|
||||||
shape_nothing: light_cyan
|
shape_nothing: light_cyan
|
||||||
|
shape_matching_brackets: { attr: u }
|
||||||
}
|
}
|
||||||
|
|
||||||
let light_theme = {
|
let light_theme = {
|
||||||
@ -231,6 +232,7 @@ let light_theme = {
|
|||||||
shape_flag: blue_bold
|
shape_flag: blue_bold
|
||||||
shape_custom: green
|
shape_custom: green
|
||||||
shape_nothing: light_cyan
|
shape_nothing: light_cyan
|
||||||
|
shape_matching_brackets: { attr: u }
|
||||||
}
|
}
|
||||||
|
|
||||||
# External completer example
|
# External completer example
|
||||||
|
Loading…
Reference in New Issue
Block a user