diff --git a/src/editor.rs b/src/editor.rs index 2f66d6e..ddb040f 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -38,6 +38,8 @@ pub struct EditorSyntax { keywords1: Vec<&'static str>, keywords2: Vec<&'static str>, singleline_comment_start: String, + multiline_comment_start: String, + multiline_comment_end: String, flags: EditorSyntaxFlags, } @@ -48,14 +50,18 @@ impl EditorSyntax { keywords1: Vec<&'static str>, keywords2: Vec<&'static str>, single_line_comment_start: &str, + multi_line_comment_start: &str, + multi_line_comment_end: &str, flags: EditorSyntaxFlags, ) -> Self { EditorSyntax { - file_type: String::from(file_type), + file_type: file_type.to_owned(), file_match, keywords1, keywords2, - singleline_comment_start: String::from(single_line_comment_start), + singleline_comment_start: single_line_comment_start.to_owned(), + multiline_comment_start: multi_line_comment_start.to_owned(), + multiline_comment_end: multi_line_comment_end.to_owned(), flags, } } @@ -65,6 +71,7 @@ impl EditorSyntax { pub enum Highlight { Normal, LineComment, + MultiLineComment, Keyword1, Keyword2, String, @@ -380,15 +387,18 @@ impl Editor { let keywords1 = ¤t_syntax.keywords1; let keywords2 = ¤t_syntax.keywords2; - let scs = current_syntax.singleline_comment_start.clone(); + let scs = ¤t_syntax.singleline_comment_start; + let mcs = ¤t_syntax.multiline_comment_start; + let mce = ¤t_syntax.multiline_comment_end; let mut prev_separator = false; let mut in_string = false; let mut str_start = '\0'; + let mut in_comment = false; let mut i = 0; let bytes = row.render.clone().into_bytes(); - while i < row.render.len() { + 'outer: while i < row.render.len() { let c = bytes[i] as char; let prev_highlight = if i > 0 { row.highlight[i - 1] @@ -398,7 +408,7 @@ impl Editor { // Single line comments if scs.len() > 0 && !in_string { - let comment = row.render.find(&scs); + let comment = row.render.find(scs); if comment.is_some() { // Pretty simple, highlight from the match to the end of the line let comment_start = comment.unwrap(); @@ -408,6 +418,47 @@ impl Editor { } } + // Multi-line comments + if mcs.len() > 0 && mce.len() > 0 && !in_string { + let mce_slice_range = if i as usize + mce.len() >= row.render.len() { + i as usize..row.render.len() + } else { + i as usize..(i as usize + mce.len()) + }; + let mcs_slice_range = if i as usize + mcs.len() >= row.render.len() { + i as usize..row.render.len() + } else { + i as usize..(i as usize + mcs.len()) + }; + if in_comment { + row.highlight[i as usize] = Highlight::MultiLineComment; + + // End of a comment + if &row.render[mce_slice_range.clone()] == mce { + for x in mce_slice_range { + row.highlight[x] = Highlight::MultiLineComment; + } + + i += mce.len(); + in_comment = false; + prev_separator = true; + continue; + } else { + i += 1; + continue; + } + } else if &row.render[mcs_slice_range.clone()] == mcs { + // Start of a multi-line comment + for x in mcs_slice_range { + row.highlight[x] = Highlight::MultiLineComment; + } + + i += mcs.len(); + in_comment = true; + continue; + } + } + // Strings if current_syntax .flags @@ -431,7 +482,7 @@ impl Editor { prev_separator = true; continue; } else { - if c == '"' || c == '\'' { + if (c == '"' || c == '\'') && prev_separator { in_string = true; str_start = c; row.highlight[i as usize] = Highlight::String; @@ -461,9 +512,22 @@ impl Editor { for &keyword in keywords1 { let matches = row.render.match_indices(keyword); for (start, _) in matches { - let end = start + keyword.len(); - for x in start..end { - row.highlight[x] = Highlight::Keyword1; + let next_char_offset = start + keyword.len() + 1; + let is_end_of_line = next_char_offset >= row.render.len(); + let next_char = if is_end_of_line { + '\0' + } else { + bytes[next_char_offset] as char + }; + + if is_separator(next_char) { + let end = start + keyword.len(); + for x in start..end { + row.highlight[x] = Highlight::Keyword1; + } + i += keyword.len(); + prev_separator = false; + continue 'outer; } } } @@ -471,9 +535,22 @@ impl Editor { for &keyword in keywords2 { let matches = row.render.match_indices(keyword); for (start, _) in matches { - let end = start + keyword.len(); - for x in start..end { - row.highlight[x] = Highlight::Keyword2; + let next_char_offset = start + keyword.len() + 1; + let is_end_of_line = next_char_offset >= row.render.len(); + let next_char = if is_end_of_line { + '\0' + } else { + bytes[next_char_offset] as char + }; + + if is_separator(next_char) { + let end = start + keyword.len(); + for x in start..end { + row.highlight[x] = Highlight::Keyword2; + } + i += keyword.len(); + prev_separator = false; + continue 'outer; } } } @@ -491,6 +568,7 @@ impl Editor { Keyword1 => 33, // Yellow Keyword2 => 32, // Green LineComment => 36, // Cyan + MultiLineComment => 36, Normal => 37, Number => 31, // Red SearchMatch => 34, // Blue @@ -815,7 +893,23 @@ impl Editor { let mut current_color: i32 = -1; for (x, ch) in output.char_indices() { - if self.rows[file_row].highlight[x] == Highlight::Normal { + if ch.is_ascii_control() { + // Display unprintable characters in inverted colors + let sym = if ch as u8 <= 26 { + ('@' as u8 + ch as u8) as char + } else { + '?' + }; + + self.append_out("\x1b[7m"); + self.append_out_char(sym); + self.append_out("\x1b[m"); + if current_color != -1 { + let code = format!("\x1b[{}m", current_color); + self.append_out(&code); + } + + } else if self.rows[file_row].highlight[x] == Highlight::Normal { if current_color != -1 { self.append_out("\x1b[39m"); current_color = -1; @@ -1276,6 +1370,8 @@ fn get_syntax_db() -> Vec { ], vec!["int", "long", "double", "float", "char", "unsigned", "signed", "void"], "//", + "/*", + "*/", EditorSyntaxFlags::HIGHLIGHT_NUMBERS | EditorSyntaxFlags::HIGHLIGHT_STRINGS, ), EditorSyntax::new( @@ -1345,21 +1441,27 @@ fn get_syntax_db() -> Vec { "f64" ], "//", + "/*", + "*/", EditorSyntaxFlags::HIGHLIGHT_NUMBERS | EditorSyntaxFlags::HIGHLIGHT_STRINGS, ), ] } -fn is_separator(c: char) -> bool { +fn is_separator(input_char: char) -> bool { + if input_char.is_ascii_whitespace() || input_char == '\0' { + return true; + } + let separator_chars = ",.()+-/*=~%<>[];"; for ch in separator_chars.chars() { - if c == ch { + if input_char == ch { return true; } } - c.is_ascii_whitespace() || c == '\0' + false } #[cfg(test)] @@ -1377,4 +1479,9 @@ mod tests { assert_eq!(editor.syntax.as_ref(), Some(&langs[0])); } + #[test] + fn is_separator_works() { + assert_eq!(is_separator(' '), true); + assert_eq!(is_separator('_'), false); + } }