diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ansi/colors.rs | 28 | ||||
| -rw-r--r-- | src/ansi/mod.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/parse/mod.rs | 99 | ||||
| -rw-r--r-- | src/run/builtin.rs | 40 | ||||
| -rw-r--r-- | src/run/mod.rs | 10 | ||||
| -rw-r--r-- | src/syntax_highlighting.rs | 53 | ||||
| -rw-r--r-- | src/variants.rs | 3 |
8 files changed, 215 insertions, 21 deletions
diff --git a/src/ansi/colors.rs b/src/ansi/colors.rs new file mode 100644 index 0000000..d14dc81 --- /dev/null +++ b/src/ansi/colors.rs @@ -0,0 +1,28 @@ +macro_rules! color { + ($($name: ident = $num:expr ;)*) => { + $( + #[allow(unused)] + pub const $name: &str = concat!("\x1b[", stringify!($num), "m"); + )* + }; +} + +color! { + RESET = 0; + BLACK_FG = 30; + BLACK_BG = 40; + RED_FG = 31; + RED_BG = 41; + GREEN_FG = 32; + GREEN_BG = 42; + YELLOW_FG = 33; + YELLOW_BG = 43; + BLUE_FG = 34; + BLUE_BG = 44; + MAGENTA_FG = 35; + MAGENTA_BG = 45; + CYAN_FG = 36; + CYAN_BG = 46; + WHITE_FG = 37; + WHITE_BG = 47; +} diff --git a/src/ansi/mod.rs b/src/ansi/mod.rs index 7a38ae4..d7e2ab3 100644 --- a/src/ansi/mod.rs +++ b/src/ansi/mod.rs @@ -1,5 +1,7 @@ use std::{collections::BTreeMap, io::Read, sync::RwLock}; +pub mod colors; + fn read1() -> Option<u8> { let mut buf = [0]; match std::io::stdin().lock().read_exact(&mut buf) { @@ -35,6 +35,7 @@ pub mod rw; pub mod serialization; pub mod syntax_highlighting; pub mod wait; +pub mod variants; use raw::*; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 10ec666..8970fc3 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,4 +1,6 @@ -use crate::{BString, PushAll, bstr}; +use pish_derive::Variants; + +use crate::{BString, PushAll, bstr, variants::Variants}; #[cfg(test)] mod test; @@ -1042,7 +1044,7 @@ impl Parse for ExpString { let end = b.loc_u32(); b.highlights.push(Highlight { span: begin.to(end), - kind: HighlightKind::String, + kind: HighlightKind::Other(OtherHighlights::String), }); } } @@ -1296,11 +1298,94 @@ pub enum ParseMode { Completion, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum HighlightKind { None, - String, Keyword(Keyword), + Other(OtherHighlights), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Variants)] +pub enum OtherHighlights { + String, +} + +impl OtherHighlights { + pub fn identifier(&self) -> &bstr { + match self { + OtherHighlights::String => b"string", + } + } +} + +impl HighlightKind { + /// all highlight kind variants *except* None + pub fn variants() -> impl Iterator<Item = HighlightKind> { + let a = Keyword::VARIANTS + .into_iter() + .cloned() + .map(HighlightKind::Keyword); + let b = OtherHighlights::VARIANTS + .into_iter() + .cloned() + .map(HighlightKind::Other); + a.chain(b) + } + + /// an unique identifier such that we can refer to that in the builtin `pish_theme` + pub fn identifier(&self) -> &bstr { + match self { + HighlightKind::None => b"default", + HighlightKind::Keyword(keyword) => keyword.identifier(), + HighlightKind::Other(other) => other.identifier(), + } + } + + pub fn from_identifier(ident: &bstr) -> Vec<HighlightKind> { + match ident { + b"keywords" => { + return Keyword::VARIANTS + .into_iter() + .cloned() + .map(HighlightKind::Keyword) + .collect(); + } + b"braces" => { + return vec![ + HighlightKind::Keyword(Keyword::OpenBrace), + HighlightKind::Keyword(Keyword::CloseBrace), + ]; + } + b"all" | b"everything" => return Self::variants().collect(), + _ => (), + } + + Self::variants() + .into_iter() + .filter(|x| x.identifier() == ident) + .collect() + } + + pub fn all_identifiers() -> Vec<BString> { + let kw = Keyword::VARIANTS.into_iter().map(Keyword::identifier); + let ot = OtherHighlights::VARIANTS + .into_iter() + .map(OtherHighlights::identifier); + let groups = [&b"keywords"[..], b"braces", b"all", b"everything"]; + kw.chain(ot) + .chain(groups) + .map(|ident| ident.to_vec()) + .collect() + } +} + +#[test] +fn no_two_highlight_kinds_share_an_identifier() { + use std::collections::HashSet; + let unique_identifiers: HashSet<BString> = HighlightKind::variants() + .map(|x| x.identifier().to_vec()) + .collect(); + assert_eq!(unique_identifiers.len(), HighlightKind::variants().count()); } pub struct Highlight { @@ -1504,7 +1589,7 @@ impl<'a> Cursor<'a> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Variants)] pub enum Keyword { If, While, @@ -1536,6 +1621,10 @@ impl Keyword { Keyword::CloseBrace => false, } } + + pub fn identifier(&self) -> &bstr { + self.as_bytes() + } } impl If<PreExpansion> { diff --git a/src/run/builtin.rs b/src/run/builtin.rs index c946472..2255997 100644 --- a/src/run/builtin.rs +++ b/src/run/builtin.rs @@ -967,6 +967,46 @@ impl Builtin for export { } } +#[derive(Copy, Clone)] +pub struct pish_theme; + +impl Builtin for pish_theme { + fn name(&self) -> &str { + "pish_theme" + } + + fn io( + &self, + session: Arc<Mutex<Session>>, + args: &[BString], + _stdin: &mut dyn Read, + stdout: &mut dyn Write, + ) -> Result { + if args.len() != 2 { + stdout.write_all(b"usage: pish_theme <kind> <color>\nwhere color is an ansi escape code,\nand where kind is one of the following: ")?; + for ident in crate::parse::HighlightKind::all_identifiers() { + stdout.write_all(&ident)?; + stdout.write_all(b" ")?; + } + stdout.write_all(b"\n")?; + return Err(Error::Exit(-1)); + } + + let mut se = session.lock().unwrap(); + match se.highlighter.set_color(&args[0], &args[1]) { + Ok(_) => Ok(()), + Err(e) => match e { + syntax_highlighting::SetColorError::NoSuchKeyword => { + stdout.write_all(b"no such kind: ")?; + stdout.write_all(&args[0])?; + stdout.write_all(b"\n")?; + Err(Error::Exit(-1)) + } + }, + } + } +} + #[cfg(debug_assertions)] mod dbg { use super::*; diff --git a/src/run/mod.rs b/src/run/mod.rs index f34b678..842a918 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -451,7 +451,14 @@ impl Executor { stdin: InputReader, stdout: OutputWriter, ) -> SpawnedCmd { - self.execute_block(parse::Block { commands: s.stmts, finished_parsing: true }, stdin, stdout) + self.execute_block( + parse::Block { + commands: s.stmts, + finished_parsing: true, + }, + stdin, + stdout, + ) } } @@ -711,6 +718,7 @@ const BUILTINS: &[&'static dyn BuiltinClone] = &[ &builtin::Here, &builtin::logo, &builtin::export, + &builtin::pish_theme, ]; pub fn builtin_map() -> HashMap<BString, &'static dyn BuiltinClone> { diff --git a/src/syntax_highlighting.rs b/src/syntax_highlighting.rs index 0b1e8c5..b76aa4b 100644 --- a/src/syntax_highlighting.rs +++ b/src/syntax_highlighting.rs @@ -1,28 +1,51 @@ -use crate::parse::{Highlight, HighlightKind, Keyword}; +use std::collections::HashMap; + +use crate::{ + BString, ansi, bstr, + parse::{Highlight, HighlightKind}, +}; pub struct Highlighter { pub enabled: bool, + colors: HashMap<HighlightKind, BString>, +} + +#[derive(Debug)] +pub enum SetColorError { + NoSuchKeyword, } impl Highlighter { pub fn new() -> Self { - Self { enabled: true } + let mut this = Self { + enabled: true, + colors: HashMap::new(), + }; + let mut sc = |a: &str, b: &str| this.set_color(a.as_bytes(), b.as_bytes()).unwrap(); + use ansi::colors::*; + sc("keywords", GREEN_FG); + sc("braces", CYAN_FG); + sc("string", MAGENTA_FG); + this } - pub fn color(&self, h: HighlightKind) -> &[u8] { - // TODO: configurable - const GREEN: &[u8] = b"\x1b[32m"; - const BLUE: &[u8] = b"\x1b[36m"; - const MAGENTA: &[u8] = b"\x1b[95m"; - const COLOR_RESET: &[u8] = b"\x1b[0m"; - match h { - HighlightKind::Keyword( - Keyword::If | Keyword::Elif | Keyword::Else | Keyword::While, - ) => GREEN, - HighlightKind::Keyword(Keyword::OpenBrace | Keyword::CloseBrace) => BLUE, - HighlightKind::String => MAGENTA, - HighlightKind::None => COLOR_RESET, + pub fn set_color(&mut self, ident: &bstr, color: &bstr) -> Result<(), SetColorError> { + let kinds = HighlightKind::from_identifier(ident); + for kind in kinds.iter() { + self.colors.insert(*kind, color.to_vec()); } + if kinds.is_empty() { + Err(SetColorError::NoSuchKeyword) + } else { + Ok(()) + } + } + + pub fn color(&self, h: HighlightKind) -> &[u8] { + self.colors + .get(&h) + .map(|c| c.as_ref()) + .unwrap_or(ansi::colors::RESET.as_bytes()) } pub fn pretty_print( diff --git a/src/variants.rs b/src/variants.rs new file mode 100644 index 0000000..f2bb0a1 --- /dev/null +++ b/src/variants.rs @@ -0,0 +1,3 @@ +pub trait Variants: 'static + Sized { + const VARIANTS: &[Self]; +} |
