From bcae2a187e934435ba44e6dc44ca73801d23babd Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 12:57:22 +0000 Subject: [PATCH 1/8] feat(cli/repl): add regex based syntax highlighter This adds a simple regex replace based highlighter to the repl editor. --- cli/repl.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 5daa122615e501..ffe12070b64a53 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -1,22 +1,28 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::colors; use crate::global_state::GlobalState; use crate::inspector::InspectorSession; use deno_core::error::AnyError; use deno_core::serde_json::json; +use regex::Captures; +use regex::Regex; use rustyline::error::ReadlineError; +use rustyline::highlight::Highlighter; use rustyline::validate::MatchingBracketValidator; use rustyline::validate::ValidationContext; use rustyline::validate::ValidationResult; use rustyline::validate::Validator; use rustyline::Editor; -use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; +use rustyline_derive::{Completer, Helper, Hinter}; +use std::borrow::Cow; use std::sync::Arc; use std::sync::Mutex; // Provides syntax specific helpers to the editor like validation for multi-line edits. -#[derive(Completer, Helper, Highlighter, Hinter)] +#[derive(Completer, Helper, Hinter)] struct Helper { + highlighter: SyntaxHighlighter, validator: MatchingBracketValidator, } @@ -29,6 +35,82 @@ impl Validator for Helper { } } +impl Highlighter for Helper { + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + hint.into() + } + + fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + self.highlighter.highlight(line, pos) + } + + fn highlight_candidate<'c>( + &self, + candidate: &'c str, + _completion: rustyline::CompletionType, + ) -> Cow<'c, str> { + self.highlighter.highlight(candidate, 0) + } + + fn highlight_char(&self, line: &str, _: usize) -> bool { + !line.is_empty() + } +} + +struct SyntaxHighlighter { + lexer: Regex, +} + +impl SyntaxHighlighter { + fn new() -> Self { + let lexer = Regex::new( + r#"(?x) + (?P(?:/\*[\s\S]*?\*/|//[^\n]*)) | + (?P(?:"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|`([^`\\]|\\.)*`)) | + (?P/(?:(?:\\/|[^\n/]))*?/) | + (?P\d+(?:\.\d+)*(?:e[+-]?\d+)*n*) | + (?P\b(?:true|false)\b) | + (?P\b(?:null)\b) | + (?P\b(?:undefined)\b) | + (?P\b(?:await|async|var|let|for|if|else|in|class|const|function|yield|return|with|case|break|switch|import|export|new|while|do|throw|catch)\b) | + "#, + ) + .unwrap(); + + return Self { lexer }; + } +} + +impl Highlighter for SyntaxHighlighter { + fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> { + self + .lexer + .replace_all(&line.to_string(), |caps: &Captures<'_>| { + if let Some(cap) = caps.name("comment") { + format!("{}", colors::gray(cap.as_str())) + } else if let Some(cap) = caps.name("string") { + format!("{}", colors::green(cap.as_str())) + } else if let Some(cap) = caps.name("regexp") { + format!("{}", colors::red(cap.as_str())) + } else if let Some(cap) = caps.name("number") { + format!("{}", colors::yellow(cap.as_str())) + } else if let Some(cap) = caps.name("boolean") { + format!("{}", colors::yellow(cap.as_str())) + } else if let Some(cap) = caps.name("null") { + format!("{}", colors::yellow(cap.as_str())) + } else if let Some(cap) = caps.name("undefined") { + format!("{}", colors::gray(cap.as_str())) + } else if let Some(cap) = caps.name("keyword") { + format!("{}", colors::cyan(cap.as_str())) + } else { + caps[0].to_string() + } + }) + .to_string() + .into() + } +} + pub async fn run( global_state: &GlobalState, mut session: Box, @@ -43,6 +125,7 @@ pub async fn run( .await?; let helper = Helper { + highlighter: SyntaxHighlighter::new(), validator: MatchingBracketValidator::new(), }; From 79e990a8c251a82edcf98c5ed08770610abcea48 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 13:14:57 +0000 Subject: [PATCH 2/8] Lint --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index ffe12070b64a53..6655c54c449e77 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -77,7 +77,7 @@ impl SyntaxHighlighter { ) .unwrap(); - return Self { lexer }; + Self { lexer } } } From ba391f978f1fab716973acb3a53634b93062458f Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 13:18:37 +0000 Subject: [PATCH 3/8] Add 'of' to keywords --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 6655c54c449e77..dc9078ac45c79a 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -72,7 +72,7 @@ impl SyntaxHighlighter { (?P\b(?:true|false)\b) | (?P\b(?:null)\b) | (?P\b(?:undefined)\b) | - (?P\b(?:await|async|var|let|for|if|else|in|class|const|function|yield|return|with|case|break|switch|import|export|new|while|do|throw|catch)\b) | + (?P\b(?:await|async|var|let|for|if|else|in|of|class|const|function|yield|return|with|case|break|switch|import|export|new|while|do|throw|catch)\b) | "#, ) .unwrap(); From 23d29ab9e3e8a77a72dd2a4d2e05d1cfd881c3b4 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 13:27:29 +0000 Subject: [PATCH 4/8] Include regexp flags in capture group --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index dc9078ac45c79a..a0a858970cb66f 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -67,7 +67,7 @@ impl SyntaxHighlighter { r#"(?x) (?P(?:/\*[\s\S]*?\*/|//[^\n]*)) | (?P(?:"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|`([^`\\]|\\.)*`)) | - (?P/(?:(?:\\/|[^\n/]))*?/) | + (?P/(?:(?:\\/|[^\n/]))*?/[gimsuy]*) | (?P\d+(?:\.\d+)*(?:e[+-]?\d+)*n*) | (?P\b(?:true|false)\b) | (?P\b(?:null)\b) | From 92b9b45e7c956f7095546f8384310236a593b935 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 13:33:15 +0000 Subject: [PATCH 5/8] Zero or one 'n' for big integer literals --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index a0a858970cb66f..08d55425f9e2f0 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -68,7 +68,7 @@ impl SyntaxHighlighter { (?P(?:/\*[\s\S]*?\*/|//[^\n]*)) | (?P(?:"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|`([^`\\]|\\.)*`)) | (?P/(?:(?:\\/|[^\n/]))*?/[gimsuy]*) | - (?P\d+(?:\.\d+)*(?:e[+-]?\d+)*n*) | + (?P\d+(?:\.\d+)*(?:e[+-]?\d+)*n?) | (?P\b(?:true|false)\b) | (?P\b(?:null)\b) | (?P\b(?:undefined)\b) | From 8572398676a6bd8fafe18ca8dff583e2b058f577 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sat, 3 Oct 2020 13:35:28 +0000 Subject: [PATCH 6/8] s/lexer/regex --- cli/repl.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 08d55425f9e2f0..31b56043839d22 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -58,12 +58,12 @@ impl Highlighter for Helper { } struct SyntaxHighlighter { - lexer: Regex, + regex: Regex, } impl SyntaxHighlighter { fn new() -> Self { - let lexer = Regex::new( + let regex = Regex::new( r#"(?x) (?P(?:/\*[\s\S]*?\*/|//[^\n]*)) | (?P(?:"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|`([^`\\]|\\.)*`)) | @@ -77,14 +77,14 @@ impl SyntaxHighlighter { ) .unwrap(); - Self { lexer } + Self { regex } } } impl Highlighter for SyntaxHighlighter { fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> { self - .lexer + .regex .replace_all(&line.to_string(), |caps: &Captures<'_>| { if let Some(cap) = caps.name("comment") { format!("{}", colors::gray(cap.as_str())) From 7a124613eee33a67f966826c6912509cc1fa348a Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sun, 4 Oct 2020 00:31:22 +0000 Subject: [PATCH 7/8] s/SyntaxHighlighter/LineHighlighter --- cli/repl.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 31b56043839d22..53872233c582a6 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -22,7 +22,7 @@ use std::sync::Mutex; // Provides syntax specific helpers to the editor like validation for multi-line edits. #[derive(Completer, Helper, Hinter)] struct Helper { - highlighter: SyntaxHighlighter, + highlighter: LineHighlighter, validator: MatchingBracketValidator, } @@ -57,11 +57,11 @@ impl Highlighter for Helper { } } -struct SyntaxHighlighter { +struct LineHighlighter { regex: Regex, } -impl SyntaxHighlighter { +impl LineHighlighter { fn new() -> Self { let regex = Regex::new( r#"(?x) @@ -81,7 +81,7 @@ impl SyntaxHighlighter { } } -impl Highlighter for SyntaxHighlighter { +impl Highlighter for LineHighlighter { fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> { self .regex @@ -125,7 +125,7 @@ pub async fn run( .await?; let helper = Helper { - highlighter: SyntaxHighlighter::new(), + highlighter: LineHighlighter::new(), validator: MatchingBracketValidator::new(), }; From dbb73f79ebd9f0af51294ffc6f8453375018d68d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 7 Oct 2020 08:14:02 +0000 Subject: [PATCH 8/8] Format --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 6adf7e9311f38b..db6518928760b6 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -7,9 +7,9 @@ use crate::worker::MainWorker; use crate::worker::Worker; use deno_core::error::AnyError; use deno_core::serde_json::json; +use deno_core::serde_json::Value; use regex::Captures; use regex::Regex; -use deno_core::serde_json::Value; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; use rustyline::validate::MatchingBracketValidator;