From 37f8ff0efc4eef02b67228d8c36e2fa5e33cd798 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 23 Jul 2021 07:50:59 +1200 Subject: [PATCH] Add highlighting --- Cargo.toml | 3 +- src/flatten.rs | 110 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/main.rs | 11 ++-- src/parser.rs | 20 +++++--- src/parser_state.rs | 25 ++++++--- src/syntax_highlight.rs | 95 ++++++++++++++++++++++++++-------- 7 files changed, 227 insertions(+), 39 deletions(-) create mode 100644 src/flatten.rs diff --git a/Cargo.toml b/Cargo.toml index a7d2388eb..7a3ea1ac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -reedline = {git = "https://github.com/jntrnr/reedline"} \ No newline at end of file +reedline = {path = "../reedline"} +nu-ansi-term = "0.32.0" \ No newline at end of file diff --git a/src/flatten.rs b/src/flatten.rs new file mode 100644 index 000000000..4a55a8dac --- /dev/null +++ b/src/flatten.rs @@ -0,0 +1,110 @@ +use crate::{Block, Expr, Expression, ParserWorkingSet, Pipeline, Span, Statement}; + +#[derive(Debug)] +pub enum FlatShape { + Garbage, + Int, + InternalCall, + External, + Literal, + Operator, + Signature, + String, + Variable, +} + +impl<'a> ParserWorkingSet<'a> { + pub fn flatten_block(&self, block: &Block) -> Vec<(Span, FlatShape)> { + let mut output = vec![]; + for stmt in &block.stmts { + output.extend(self.flatten_statement(stmt)); + } + output + } + + pub fn flatten_statement(&self, stmt: &Statement) -> Vec<(Span, FlatShape)> { + match stmt { + Statement::Expression(expr) => self.flatten_expression(expr), + Statement::Pipeline(pipeline) => self.flatten_pipeline(pipeline), + } + } + + pub fn flatten_expression(&self, expr: &Expression) -> Vec<(Span, FlatShape)> { + match &expr.expr { + Expr::BinaryOp(lhs, op, rhs) => { + let mut output = vec![]; + output.extend(self.flatten_expression(&lhs)); + output.extend(self.flatten_expression(&op)); + output.extend(self.flatten_expression(&rhs)); + output + } + Expr::Block(block_id) => self.flatten_block( + self.get_block(*block_id) + .expect("internal error: missing block"), + ), + Expr::Call(call) => { + let mut output = vec![]; + output.push((call.head, FlatShape::InternalCall)); + for positional in &call.positional { + output.extend(self.flatten_expression(positional)); + } + output + } + Expr::ExternalCall(..) => { + vec![(expr.span, FlatShape::External)] + } + Expr::Garbage => { + vec![(expr.span, FlatShape::Garbage)] + } + Expr::Int(_) => { + vec![(expr.span, FlatShape::Int)] + } + Expr::List(list) => { + let mut output = vec![]; + for l in list { + output.extend(self.flatten_expression(l)); + } + output + } + Expr::Literal(_) => { + vec![(expr.span, FlatShape::Literal)] + } + Expr::Operator(_) => { + vec![(expr.span, FlatShape::Operator)] + } + Expr::Signature(_) => { + vec![(expr.span, FlatShape::Signature)] + } + Expr::String(_) => { + vec![(expr.span, FlatShape::String)] + } + Expr::Subexpression(block_id) => self.flatten_block( + self.get_block(*block_id) + .expect("internal error: missing block"), + ), + Expr::Table(headers, cells) => { + let mut output = vec![]; + for e in headers { + output.extend(self.flatten_expression(e)); + } + for row in cells { + for expr in row { + output.extend(self.flatten_expression(expr)); + } + } + output + } + Expr::Var(_) => { + vec![(expr.span, FlatShape::Variable)] + } + } + } + + pub fn flatten_pipeline(&self, pipeline: &Pipeline) -> Vec<(Span, FlatShape)> { + let mut output = vec![]; + for expr in &pipeline.expressions { + output.extend(self.flatten_expression(expr)) + } + output + } +} diff --git a/src/lib.rs b/src/lib.rs index 53eb96776..313efe2c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod declaration; mod eval; +mod flatten; mod lex; mod lite_parse; mod parse_error; @@ -22,3 +23,4 @@ pub use parser::{ pub use parser_state::{BlockId, DeclId, ParserState, ParserWorkingSet, VarId}; pub use signature::Signature; pub use span::Span; +pub use syntax_highlight::NuHighlighter; diff --git a/src/main.rs b/src/main.rs index e8eba65c1..2193b3afd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, rc::Rc}; -use engine_q::{ParserState, ParserWorkingSet, Signature, SyntaxShape}; +use engine_q::{NuHighlighter, ParserState, ParserWorkingSet, Signature, SyntaxShape}; fn main() -> std::io::Result<()> { let parser_state = Rc::new(RefCell::new(ParserState::new())); @@ -109,8 +109,11 @@ fn main() -> std::io::Result<()> { } else { use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; - let mut line_editor = - Reedline::new().with_history(Box::new(FileBackedHistory::new(1000)))?; + let mut line_editor = Reedline::new() + .with_history(Box::new(FileBackedHistory::new(1000)))? + .with_highlighter(Box::new(NuHighlighter { + parser_state: parser_state.clone(), + })); let prompt = DefaultPrompt::new(1); let mut current_line = 1; @@ -132,7 +135,7 @@ fn main() -> std::io::Result<()> { s.as_bytes(), false, ); - println!("{:#?}", output); + println!("{:?}", output); println!("Error: {:?}", err); working_set.render() }; diff --git a/src/parser.rs b/src/parser.rs index 3598bbeaa..acc75f796 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -104,6 +104,7 @@ pub enum Operator { pub struct Call { /// identifier of the declaration to call pub decl_id: DeclId, + pub head: Span, pub positional: Vec, pub named: Vec<(String, Option)>, } @@ -118,6 +119,7 @@ impl Call { pub fn new() -> Call { Self { decl_id: 0, + head: Span::unknown(), positional: vec![], named: vec![], } @@ -557,6 +559,7 @@ impl<'a> ParserWorkingSet<'a> { let mut call = Call::new(); call.decl_id = decl_id; + call.head = command_span; let decl = self .get_decl(decl_id) @@ -616,18 +619,19 @@ impl<'a> ParserWorkingSet<'a> { let end = if decl.signature.rest_positional.is_some() { spans.len() } else { - println!("num_positionals: {}", decl.signature.num_positionals()); - println!("positional_idx: {}", positional_idx); - println!("spans.len(): {}", spans.len()); - println!("spans_idx: {}", spans_idx); + // println!("num_positionals: {}", decl.signature.num_positionals()); + // println!("positional_idx: {}", positional_idx); + // println!("spans.len(): {}", spans.len()); + // println!("spans_idx: {}", spans_idx); let remainder = decl.signature.num_positionals() - positional_idx; - if remainder > spans.len() { + if remainder >= spans.len() { spans.len() } else { spans.len() - remainder + 1 } }; + // println!("end: {}", end); let (arg, err) = self.parse_multispan_value(&spans[..end], &mut spans_idx, positional.shape); @@ -1669,6 +1673,10 @@ impl<'a> ParserWorkingSet<'a> { if idx == spans.len() { // Handle broken math expr `1 +` etc error = error.or(Some(ParseError::IncompleteMathExpression(spans[idx - 1]))); + + expr_stack.push(Expression::garbage(spans[idx - 1])); + expr_stack.push(Expression::garbage(spans[idx - 1])); + break; } @@ -1947,7 +1955,7 @@ impl<'a> ParserWorkingSet<'a> { #[cfg(test)] mod tests { - use crate::{parser_state, ParseError, ParserState, Signature}; + use crate::{ParseError, ParserState, Signature}; use super::*; diff --git a/src/parser_state.rs b/src/parser_state.rs index 59066e849..de618e9bf 100644 --- a/src/parser_state.rs +++ b/src/parser_state.rs @@ -97,6 +97,10 @@ impl ParserState { self.decls.get(decl_id) } + pub fn get_block(&self, block_id: BlockId) -> Option<&Block> { + self.blocks.get(block_id) + } + pub fn next_span_start(&self) -> usize { self.file_contents.len() } @@ -310,21 +314,30 @@ impl<'a> ParserWorkingSet<'a> { next_id } - pub fn get_variable(&self, var_id: VarId) -> Option { + pub fn get_variable(&self, var_id: VarId) -> Option<&Type> { let num_permanent_vars = self.permanent_state.num_vars(); if var_id < num_permanent_vars { - self.permanent_state.get_var(var_id).cloned() + self.permanent_state.get_var(var_id) } else { - self.delta.vars.get(var_id - num_permanent_vars).cloned() + self.delta.vars.get(var_id - num_permanent_vars) } } - pub fn get_decl(&self, decl_id: DeclId) -> Option { + pub fn get_decl(&self, decl_id: DeclId) -> Option<&Declaration> { let num_permanent_decls = self.permanent_state.num_decls(); if decl_id < num_permanent_decls { - self.permanent_state.get_decl(decl_id).cloned() + self.permanent_state.get_decl(decl_id) } else { - self.delta.decls.get(decl_id - num_permanent_decls).cloned() + self.delta.decls.get(decl_id - num_permanent_decls) + } + } + + pub fn get_block(&self, block_id: BlockId) -> Option<&Block> { + let num_permanent_blocks = self.permanent_state.num_blocks(); + if block_id < num_permanent_blocks { + self.permanent_state.get_block(block_id) + } else { + self.delta.blocks.get(block_id - num_permanent_blocks) } } diff --git a/src/syntax_highlight.rs b/src/syntax_highlight.rs index 8cddcf2ef..5474479ce 100644 --- a/src/syntax_highlight.rs +++ b/src/syntax_highlight.rs @@ -1,36 +1,87 @@ +use crate::flatten::FlatShape; +use crate::{ParserState, ParserWorkingSet}; +use nu_ansi_term::Style; +use reedline::{Highlighter, StyledText}; use std::{cell::RefCell, rc::Rc}; -use crate::{Block, Expr, Expression, ParserState, ParserWorkingSet, Statement}; - -struct Highlighter { - parser_state: Rc>, +pub struct NuHighlighter { + pub parser_state: Rc>, } -impl Highlighter { - fn syntax_highlight(&self, input: &[u8]) { - let block = { +impl Highlighter for NuHighlighter { + fn highlight(&self, line: &str) -> StyledText { + let (shapes, global_span_offset) = { let parser_state = self.parser_state.borrow(); let mut working_set = ParserWorkingSet::new(&*parser_state); - let (block, _) = working_set.parse_source(input, false); + let (block, _) = working_set.parse_source(line.as_bytes(), false); - block + let shapes = working_set.flatten_block(&block); + (shapes, parser_state.next_span_start()) }; - // let (block, _) = working_set.parse_source(input, false); + let mut output = StyledText::default(); + let mut last_seen_span = global_span_offset; - // for stmt in &block.stmts { - // match stmt { - // Statement::Expression(expr) => { + for shape in &shapes { + if shape.0.end <= last_seen_span { + // We've already output something for this span + // so just skip this one + continue; + } + if shape.0.start > last_seen_span { + let gap = line + [(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)] + .to_string(); + output.push((Style::new(), gap)); + } - // } - // } - // } - // No merge at the end because this parse is speculative - } + let next_token = line + [(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)] + .to_string(); + match shape.1 { + FlatShape::External => output.push((Style::new().bold(), next_token)), + FlatShape::Garbage => output.push(( + Style::new() + .fg(nu_ansi_term::Color::White) + .on(nu_ansi_term::Color::Red) + .bold(), + next_token, + )), + FlatShape::InternalCall => output.push(( + Style::new().fg(nu_ansi_term::Color::LightBlue).bold(), + next_token, + )), + FlatShape::Int => { + output.push((Style::new().fg(nu_ansi_term::Color::Green), next_token)) + } + FlatShape::Literal => { + output.push((Style::new().fg(nu_ansi_term::Color::Blue), next_token)) + } + FlatShape::Operator => output.push(( + Style::new().fg(nu_ansi_term::Color::LightPurple).bold(), + next_token, + )), + FlatShape::Signature => output.push(( + Style::new().fg(nu_ansi_term::Color::Green).bold(), + next_token, + )), + FlatShape::String => output.push(( + Style::new().fg(nu_ansi_term::Color::Yellow).bold(), + next_token, + )), + FlatShape::Variable => output.push(( + Style::new().fg(nu_ansi_term::Color::Blue).bold(), + next_token, + )), + } + last_seen_span = shape.0.end; + } - fn highlight_expression(expression: &Expression) { - // match &expression.expr { - // Expr::BinaryOp() - // } + let remainder = line[(last_seen_span - global_span_offset)..].to_string(); + if !remainder.is_empty() { + output.push((Style::new(), remainder)); + } + + output } }