Start of multiline comment highlighting

This commit is contained in:
Timothy Warren 2019-09-05 12:09:53 -04:00
parent 2cecadb3aa
commit ca51d8f1f5

View File

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