use super::{Query, QueryCursor};
use crate::parser_v4::ParseNode;
use std::collections::HashMap;
pub mod capture_names {
pub const COMMENT: &str = "comment";
pub const STRING: &str = "string";
pub const NUMBER: &str = "number";
pub const KEYWORD: &str = "keyword";
pub const FUNCTION: &str = "function";
pub const FUNCTION_CALL: &str = "function.call";
pub const METHOD: &str = "method";
pub const METHOD_CALL: &str = "method.call";
pub const VARIABLE: &str = "variable";
pub const VARIABLE_BUILTIN: &str = "variable.builtin";
pub const CONSTANT: &str = "constant";
pub const CONSTANT_BUILTIN: &str = "constant.builtin";
pub const TYPE: &str = "type";
pub const TYPE_BUILTIN: &str = "type.builtin";
pub const PROPERTY: &str = "property";
pub const OPERATOR: &str = "operator";
pub const PUNCTUATION_BRACKET: &str = "punctuation.bracket";
pub const PUNCTUATION_DELIMITER: &str = "punctuation.delimiter";
pub const PUNCTUATION_SPECIAL: &str = "punctuation.special";
pub const ATTRIBUTE: &str = "attribute";
pub const NAMESPACE: &str = "namespace";
pub const MODULE: &str = "module";
pub const LABEL: &str = "label";
pub const TAG: &str = "tag";
pub const ERROR: &str = "error";
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Highlight {
pub start_byte: usize,
pub end_byte: usize,
pub highlight: String,
}
pub struct Highlighter {
query: Query,
highlight_map: HashMap<u32, String>,
}
impl Highlighter {
pub fn new(query: Query) -> Self {
let mut highlight_map = HashMap::new();
for (name, &index) in &query.capture_names {
highlight_map.insert(index, name.clone());
}
Highlighter {
query,
highlight_map,
}
}
pub fn highlight(&self, root: &ParseNode) -> Vec<Highlight> {
let mut highlights = Vec::new();
let mut cursor = QueryCursor::new();
let matches = cursor.collect_matches(&self.query, root);
for query_match in matches {
for capture in query_match.captures {
if let Some(highlight_name) = self.highlight_map.get(&capture.index) {
highlights.push(Highlight {
start_byte: capture.node.start_byte,
end_byte: capture.node.end_byte,
highlight: highlight_name.clone(),
});
}
}
}
highlights.sort_by_key(|h| (h.start_byte, h.end_byte));
self.remove_overlaps(&mut highlights);
highlights
}
fn remove_overlaps(&self, highlights: &mut Vec<Highlight>) {
if highlights.is_empty() {
return;
}
let mut result = Vec::new();
let mut current = highlights[0].clone();
for highlight in highlights.iter().skip(1) {
if highlight.start_byte >= current.end_byte {
result.push(current);
current = highlight.clone();
} else if highlight.end_byte <= current.end_byte {
result.push(highlight.clone());
if highlight.start_byte > current.start_byte {
result.push(Highlight {
start_byte: current.start_byte,
end_byte: highlight.start_byte,
highlight: current.highlight.clone(),
});
}
if highlight.end_byte < current.end_byte {
current.start_byte = highlight.end_byte;
} else {
current = highlights[highlights.len() - 1].clone(); }
} else {
if highlight.start_byte > current.start_byte {
result.push(Highlight {
start_byte: current.start_byte,
end_byte: highlight.start_byte,
highlight: current.highlight.clone(),
});
}
current = highlight.clone();
}
}
if current.start_byte < current.end_byte {
result.push(current);
}
*highlights = result;
}
}
#[derive(Debug, Clone)]
pub struct Theme {
pub colors: HashMap<String, Color>,
pub default_color: Color,
pub background_color: Color,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Color {
pub fn new(r: u8, g: u8, b: u8) -> Self {
Color { r, g, b }
}
pub fn to_hex(&self) -> String {
format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
}
}
impl Theme {
pub fn dark() -> Self {
let mut colors = HashMap::new();
colors.insert(capture_names::COMMENT.to_string(), Color::new(106, 153, 85));
colors.insert(capture_names::STRING.to_string(), Color::new(206, 145, 120));
colors.insert(capture_names::NUMBER.to_string(), Color::new(181, 206, 168));
colors.insert(
capture_names::KEYWORD.to_string(),
Color::new(197, 134, 192),
);
colors.insert(
capture_names::FUNCTION.to_string(),
Color::new(220, 220, 170),
);
colors.insert(
capture_names::VARIABLE.to_string(),
Color::new(156, 220, 254),
);
colors.insert(
capture_names::CONSTANT.to_string(),
Color::new(79, 193, 255),
);
colors.insert(capture_names::TYPE.to_string(), Color::new(78, 201, 176));
colors.insert(
capture_names::OPERATOR.to_string(),
Color::new(212, 212, 212),
);
colors.insert(
capture_names::PUNCTUATION_BRACKET.to_string(),
Color::new(212, 212, 212),
);
colors.insert(capture_names::ERROR.to_string(), Color::new(244, 71, 71));
Theme {
colors,
default_color: Color::new(212, 212, 212),
background_color: Color::new(30, 30, 30),
}
}
pub fn light() -> Self {
let mut colors = HashMap::new();
colors.insert(capture_names::COMMENT.to_string(), Color::new(0, 128, 0));
colors.insert(capture_names::STRING.to_string(), Color::new(163, 21, 21));
colors.insert(capture_names::NUMBER.to_string(), Color::new(9, 134, 88));
colors.insert(capture_names::KEYWORD.to_string(), Color::new(0, 0, 255));
colors.insert(capture_names::FUNCTION.to_string(), Color::new(121, 94, 38));
colors.insert(capture_names::VARIABLE.to_string(), Color::new(0, 16, 128));
colors.insert(
capture_names::CONSTANT.to_string(),
Color::new(38, 127, 153),
);
colors.insert(capture_names::TYPE.to_string(), Color::new(38, 127, 153));
colors.insert(capture_names::OPERATOR.to_string(), Color::new(0, 0, 0));
colors.insert(
capture_names::PUNCTUATION_BRACKET.to_string(),
Color::new(0, 0, 0),
);
colors.insert(capture_names::ERROR.to_string(), Color::new(255, 0, 0));
Theme {
colors,
default_color: Color::new(0, 0, 0),
background_color: Color::new(255, 255, 255),
}
}
pub fn get_color(&self, highlight: &str) -> Color {
self.colors
.get(highlight)
.copied()
.unwrap_or(self.default_color)
}
}
pub mod queries {
pub const RUST_HIGHLIGHTS: &str = r#"
; Comments
(line_comment) @comment
(block_comment) @comment
; Strings
(string_literal) @string
(char_literal) @string
; Numbers
(integer_literal) @number
(float_literal) @number
; Keywords
[
"as" "async" "await" "break" "const" "continue" "crate" "dyn"
"else" "enum" "extern" "false" "fn" "for" "if" "impl" "in"
"let" "loop" "match" "mod" "move" "mut" "pub" "ref" "return"
"self" "Self" "static" "struct" "super" "trait" "true" "type"
"unsafe" "use" "where" "while"
] @keyword
; Functions
(function_item name: (identifier) @function)
(call_expression function: (identifier) @function.call)
; Types
(type_identifier) @type
(primitive_type) @type.builtin
; Variables
(identifier) @variable
; Operators
[
"+" "-" "*" "/" "%" "^" "!" "&" "|" "&&" "||"
"<<" ">>" "==" "!=" "<" "<=" ">" ">="
"=" "+=" "-=" "*=" "/=" "%=" "^=" "&=" "|="
"<<=" ">>=" "?" "=>" "->" "::" ".." "..="
] @operator
; Punctuation
["(" ")" "[" "]" "{" "}"] @punctuation.bracket
["." "," ":" ";"] @punctuation.delimiter
"#;
pub const PYTHON_HIGHLIGHTS: &str = r#"
; Comments
(comment) @comment
; Strings
(string) @string
; Numbers
(integer) @number
(float) @number
; Keywords
[
"and" "as" "assert" "async" "await" "break" "class" "continue"
"def" "del" "elif" "else" "except" "finally" "for" "from"
"global" "if" "import" "in" "is" "lambda" "nonlocal" "not"
"or" "pass" "raise" "return" "try" "while" "with" "yield"
] @keyword
; Functions
(function_definition name: (identifier) @function)
(call function: (identifier) @function.call)
; Constants
(true) @constant.builtin
(false) @constant.builtin
(none) @constant.builtin
; Variables
(identifier) @variable
"#;
}