Finish tutorial, much refactoring
This commit is contained in:
parent
ca51d8f1f5
commit
389a526f41
485
src/editor.rs
485
src/editor.rs
@ -6,9 +6,10 @@ use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::ops::Range;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use self::EditorKey::*;
|
||||
use self::KeyCode::*;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Defines
|
||||
@ -25,75 +26,82 @@ const KILO_QUIT_TIMES: u8 = 3;
|
||||
// bit flag alternative
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct EditorSyntaxFlags: u32 {
|
||||
pub struct SyntaxFlags: u32 {
|
||||
const HIGHLIGHT_NUMBERS = 0b00000001;
|
||||
const HIGHLIGHT_STRINGS = 0b00000010;
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for language syntax highlighting
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct EditorSyntax {
|
||||
pub struct Syntax {
|
||||
/// Language name, to be shown in status bar
|
||||
file_type: String,
|
||||
|
||||
/// File extensions
|
||||
file_match: Vec<&'static str>,
|
||||
|
||||
/// Keywords
|
||||
keywords1: Vec<&'static str>,
|
||||
|
||||
/// Type and/or secondary keywords
|
||||
keywords2: Vec<&'static str>,
|
||||
|
||||
/// How does a single line comment start?
|
||||
singleline_comment_start: String,
|
||||
|
||||
/// How does a multline comment start?
|
||||
multiline_comment_start: String,
|
||||
|
||||
/// How does a multiline comment end?
|
||||
multiline_comment_end: String,
|
||||
flags: EditorSyntaxFlags,
|
||||
}
|
||||
|
||||
impl EditorSyntax {
|
||||
pub fn new(
|
||||
file_type: &str,
|
||||
file_match: Vec<&'static str>,
|
||||
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: file_type.to_owned(),
|
||||
file_match,
|
||||
keywords1,
|
||||
keywords2,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for what to highlight
|
||||
flags: SyntaxFlags,
|
||||
}
|
||||
|
||||
/// Syntax highlighting token types
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Highlight {
|
||||
/// No highlighting
|
||||
Normal,
|
||||
|
||||
/// Single line comments
|
||||
LineComment,
|
||||
|
||||
/// Multiple line comments
|
||||
MultiLineComment,
|
||||
|
||||
/// Language keywords
|
||||
Keyword1,
|
||||
|
||||
/// Language types/ secondary keywords
|
||||
Keyword2,
|
||||
|
||||
/// Single-line strings
|
||||
String,
|
||||
|
||||
/// Numbers
|
||||
Number,
|
||||
|
||||
/// Search results
|
||||
SearchMatch,
|
||||
}
|
||||
|
||||
/// A representation of a line in the editor
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EditorRow {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Row {
|
||||
/// The 'raw' representation of the original characters
|
||||
chars: String,
|
||||
|
||||
/// The display characters for the editor
|
||||
render: String,
|
||||
|
||||
/// The highlighting type for each character
|
||||
highlight: Vec<Highlight>,
|
||||
}
|
||||
|
||||
impl EditorRow {
|
||||
pub fn new(chars: &str) -> Self {
|
||||
let mut instance = EditorRow::default();
|
||||
instance.chars = chars.to_owned();
|
||||
|
||||
instance
|
||||
}
|
||||
/// Are we currently highlighting a multi-line comment?
|
||||
highlight_comment_start: bool,
|
||||
}
|
||||
|
||||
/// Main structure for the editor
|
||||
@ -107,12 +115,12 @@ pub struct Editor {
|
||||
row_offset: usize,
|
||||
screen_cols: usize,
|
||||
screen_rows: usize,
|
||||
rows: Vec<EditorRow>,
|
||||
rows: Vec<Row>,
|
||||
dirty: u64,
|
||||
filename: String,
|
||||
status_message: String,
|
||||
status_message_time: Instant,
|
||||
syntax: Option<EditorSyntax>,
|
||||
syntax: Option<Syntax>,
|
||||
|
||||
// Properties not present in C version
|
||||
output_buffer: String,
|
||||
@ -125,7 +133,7 @@ pub struct Editor {
|
||||
|
||||
/// Keycode mapping enum
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum EditorKey<T> {
|
||||
pub enum KeyCode<T> {
|
||||
Enter,
|
||||
Escape,
|
||||
Backspace,
|
||||
@ -144,11 +152,44 @@ pub enum EditorKey<T> {
|
||||
OtherKey(T),
|
||||
}
|
||||
|
||||
impl EditorKey<char> {
|
||||
impl Syntax {
|
||||
pub fn new(
|
||||
file_type: &str,
|
||||
file_match: Vec<&'static str>,
|
||||
keywords1: Vec<&'static str>,
|
||||
keywords2: Vec<&'static str>,
|
||||
single_line_comment_start: &str,
|
||||
multi_line_comment_start: &str,
|
||||
multi_line_comment_end: &str,
|
||||
flags: SyntaxFlags,
|
||||
) -> Self {
|
||||
Syntax {
|
||||
file_type: file_type.to_owned(),
|
||||
file_match,
|
||||
keywords1,
|
||||
keywords2,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Row {
|
||||
pub fn new(chars: &str) -> Self {
|
||||
let mut instance = Row::default();
|
||||
instance.chars = chars.to_owned();
|
||||
|
||||
instance
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCode<char> {
|
||||
pub fn unwrap(self) -> char {
|
||||
match self {
|
||||
self::OtherKey(val) => val,
|
||||
_ => panic!("called `EditorKey::unwrap()` on a `None` value"),
|
||||
_ => panic!("called `KeyCode::unwrap()` on a `None` value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,7 +242,7 @@ impl Editor {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// Convert stdin to specific keypresses
|
||||
fn read_key(&mut self) -> Option<EditorKey<char>> {
|
||||
fn read_key(&mut self) -> Option<KeyCode<char>> {
|
||||
// --------------------------------------------------------------------
|
||||
// Match single character
|
||||
// --------------------------------------------------------------------
|
||||
@ -225,19 +266,18 @@ impl Editor {
|
||||
let first_str = first_str.unwrap();
|
||||
|
||||
// Read the first character, if it isn't escape, just return it
|
||||
let mut chars = first_str.chars();
|
||||
let char = chars.next();
|
||||
if char.is_none() {
|
||||
return None;
|
||||
}
|
||||
let char = char.unwrap();
|
||||
match char {
|
||||
'\0' => return None,
|
||||
'\x1b' => (),
|
||||
'\x08' => return Some(Backspace),
|
||||
'\x7f' => return Some(Backspace),
|
||||
'\r' => return Some(Enter),
|
||||
c => return Some(OtherKey(c)),
|
||||
let mut chs = first_str.chars();
|
||||
let ch = chs.next();
|
||||
match ch {
|
||||
Some(ch) => match ch {
|
||||
'\0' => return None,
|
||||
'\x1b' => (),
|
||||
'\x08' => return Some(Backspace),
|
||||
'\x7f' => return Some(Backspace),
|
||||
'\r' => return Some(Enter),
|
||||
_ => return Some(OtherKey(ch)),
|
||||
},
|
||||
None => return None,
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@ -261,18 +301,18 @@ impl Editor {
|
||||
}
|
||||
let seq_str = seq_str.unwrap();
|
||||
|
||||
let mut input: Vec<EditorKey<char>> = vec![];
|
||||
let mut input: Vec<KeyCode<char>> = vec![];
|
||||
|
||||
for char in seq_str.chars() {
|
||||
for ch in seq_str.chars() {
|
||||
// Since the fixed array is always filled, there
|
||||
// will be null characters. Ignore these.
|
||||
if char == '\0' {
|
||||
if ch == '\0' {
|
||||
continue;
|
||||
}
|
||||
|
||||
input.push(match char {
|
||||
input.push(match ch {
|
||||
'\x1b' => Escape,
|
||||
_ => OtherKey(char),
|
||||
_ => OtherKey(ch),
|
||||
});
|
||||
}
|
||||
|
||||
@ -372,7 +412,20 @@ impl Editor {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
fn update_syntax(&mut self, index: usize) {
|
||||
let row = &mut self.rows[index];
|
||||
let rows = &mut self.rows;
|
||||
|
||||
let prev_row = if index > 0 {
|
||||
// I shouldn't have to clone this, but the lifetime is
|
||||
// different than the `row` variable above, so it
|
||||
// can't be a immutable borrow. It also can't be a
|
||||
// mutable borrow, because it would be considered a
|
||||
// second mutable borrow
|
||||
Some((&mut rows[index - 1]).clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let row = &mut rows[index];
|
||||
row.highlight = vec![Highlight::Normal; row.render.len()];
|
||||
|
||||
if self.syntax.is_none() {
|
||||
@ -394,7 +447,7 @@ impl Editor {
|
||||
let mut prev_separator = false;
|
||||
let mut in_string = false;
|
||||
let mut str_start = '\0';
|
||||
let mut in_comment = false;
|
||||
let mut in_comment = prev_row.map_or(false, |row| row.highlight_comment_start);
|
||||
|
||||
let mut i = 0;
|
||||
let bytes = row.render.clone().into_bytes();
|
||||
@ -407,37 +460,33 @@ impl Editor {
|
||||
};
|
||||
|
||||
// Single line comments
|
||||
if scs.len() > 0 && !in_string {
|
||||
let comment = row.render.find(scs);
|
||||
if comment.is_some() {
|
||||
if scs.len() > 0 && !in_string && !in_comment {
|
||||
let range = get_slice_range(i as usize, scs.len(), row.render.len());
|
||||
if &row.render[range] == scs {
|
||||
// Pretty simple, highlight from the match to the end of the line
|
||||
let comment_start = comment.unwrap();
|
||||
for x in comment_start..row.render.len() {
|
||||
row.highlight[x] = Highlight::LineComment;
|
||||
}
|
||||
highlight_range(
|
||||
&mut row.highlight,
|
||||
i as usize..row.render.len(),
|
||||
Highlight::LineComment,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
};
|
||||
let mce_slice_range = get_slice_range(i as usize, mce.len(), row.render.len());
|
||||
let mcs_slice_range = get_slice_range(i as usize, mcs.len(), row.render.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;
|
||||
}
|
||||
highlight_range(
|
||||
&mut row.highlight,
|
||||
mce_slice_range,
|
||||
Highlight::MultiLineComment,
|
||||
);
|
||||
|
||||
i += mce.len();
|
||||
in_comment = false;
|
||||
@ -449,9 +498,11 @@ impl Editor {
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
highlight_range(
|
||||
&mut row.highlight,
|
||||
mcs_slice_range,
|
||||
Highlight::MultiLineComment,
|
||||
);
|
||||
|
||||
i += mcs.len();
|
||||
in_comment = true;
|
||||
@ -462,7 +513,7 @@ impl Editor {
|
||||
// Strings
|
||||
if current_syntax
|
||||
.flags
|
||||
.contains(EditorSyntaxFlags::HIGHLIGHT_STRINGS)
|
||||
.contains(SyntaxFlags::HIGHLIGHT_STRINGS)
|
||||
{
|
||||
if in_string {
|
||||
row.highlight[i as usize] = Highlight::String;
|
||||
@ -495,7 +546,7 @@ impl Editor {
|
||||
// Numbers
|
||||
if current_syntax
|
||||
.flags
|
||||
.contains(EditorSyntaxFlags::HIGHLIGHT_NUMBERS)
|
||||
.contains(SyntaxFlags::HIGHLIGHT_NUMBERS)
|
||||
{
|
||||
if (c.is_ascii_digit() && (prev_separator || prev_highlight == Highlight::Number))
|
||||
|| (c == '.' && prev_highlight == Highlight::Number)
|
||||
@ -510,48 +561,38 @@ impl Editor {
|
||||
// Keywords
|
||||
if prev_separator {
|
||||
for &keyword in keywords1 {
|
||||
let matches = row.render.match_indices(keyword);
|
||||
for (start, _) in matches {
|
||||
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
|
||||
};
|
||||
let search_range = get_slice_range(i as usize, keyword.len(), row.render.len());
|
||||
|
||||
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;
|
||||
}
|
||||
let next_char_offset = i as usize + 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 &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
||||
highlight_range(&mut row.highlight, search_range, Highlight::Keyword1);
|
||||
i += keyword.len();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for &keyword in keywords2 {
|
||||
let matches = row.render.match_indices(keyword);
|
||||
for (start, _) in matches {
|
||||
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
|
||||
};
|
||||
let search_range = get_slice_range(i as usize, keyword.len(), row.render.len());
|
||||
|
||||
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;
|
||||
}
|
||||
let next_char_offset = i as usize + 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 &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
||||
highlight_range(&mut row.highlight, search_range, Highlight::Keyword2);
|
||||
i += keyword.len();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -559,6 +600,15 @@ impl Editor {
|
||||
prev_separator = is_separator(c);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let changed = row.highlight_comment_start != in_comment;
|
||||
row.highlight_comment_start = in_comment;
|
||||
|
||||
// If a multi-line comment is opened or closed,
|
||||
// update the next row as well.
|
||||
if changed && index + 1 < self.rows.len() {
|
||||
self.update_syntax(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax_to_color(syntax_type: Highlight) -> i32 {
|
||||
@ -615,7 +665,7 @@ impl Editor {
|
||||
fn prompt(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
cb: Option<&mut dyn Fn(&mut Self, &str, EditorKey<char>)>,
|
||||
cb: Option<&mut dyn Fn(&mut Self, &str, KeyCode<char>)>,
|
||||
) -> String {
|
||||
let mut buffer = String::new();
|
||||
let default_cb = &mut Self::_noop_prompt_cb;
|
||||
@ -666,9 +716,9 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn _noop_prompt_cb(&mut self, _: &str, _: EditorKey<char>) {}
|
||||
fn _noop_prompt_cb(&mut self, _: &str, _: KeyCode<char>) {}
|
||||
|
||||
fn move_cursor(&mut self, key: &EditorKey<char>) {
|
||||
fn move_cursor(&mut self, key: &KeyCode<char>) {
|
||||
let row = self.rows.get(self.cursor_y);
|
||||
match key {
|
||||
ArrowLeft => {
|
||||
@ -718,7 +768,7 @@ impl Editor {
|
||||
}
|
||||
|
||||
/// Route user input to the appropriate handler method
|
||||
pub fn process_keypress(&mut self) -> Option<EditorKey<char>> {
|
||||
pub fn process_keypress(&mut self) -> Option<KeyCode<char>> {
|
||||
let key = self.read_key();
|
||||
if key.is_some() {
|
||||
let char = key.unwrap();
|
||||
@ -787,14 +837,14 @@ impl Editor {
|
||||
Some(OtherKey('\0'))
|
||||
}
|
||||
|
||||
fn _del_or_backspace(&mut self, key: EditorKey<char>) {
|
||||
fn _del_or_backspace(&mut self, key: KeyCode<char>) {
|
||||
if key == DeleteKey {
|
||||
self.move_cursor(&ArrowRight);
|
||||
}
|
||||
self.delete_char();
|
||||
}
|
||||
|
||||
fn _page_up_or_down(&mut self, key: EditorKey<char>) {
|
||||
fn _page_up_or_down(&mut self, key: KeyCode<char>) {
|
||||
let mut times = self.screen_rows;
|
||||
|
||||
// Update the cursor position
|
||||
@ -908,7 +958,6 @@ impl Editor {
|
||||
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");
|
||||
@ -1076,7 +1125,7 @@ impl Editor {
|
||||
cx
|
||||
}
|
||||
|
||||
/// Convert tab characters to spaces for display
|
||||
/// Convert file characters to their display equivalents
|
||||
fn update_row(&mut self, index: usize) {
|
||||
let row = &mut self.rows[index];
|
||||
let str = row.chars.clone();
|
||||
@ -1085,17 +1134,18 @@ impl Editor {
|
||||
let str = str.replace('\t', " ");
|
||||
row.render = str;
|
||||
|
||||
// Syntax highlighting
|
||||
self.update_syntax(index);
|
||||
}
|
||||
|
||||
fn insert_row(&mut self, at: usize, row: &str) {
|
||||
fn insert_row(&mut self, at: usize, content: &str) {
|
||||
if at > self.rows.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
let row = EditorRow::new(row);
|
||||
self.rows.insert(at, row);
|
||||
let row = Row::new(content);
|
||||
|
||||
self.rows.insert(at, row);
|
||||
self.update_row(at);
|
||||
|
||||
self.dirty += 1;
|
||||
@ -1275,7 +1325,7 @@ impl Editor {
|
||||
// Find
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
fn find_callback(&mut self, query: &str, key: EditorKey<char>) {
|
||||
fn find_callback(&mut self, query: &str, key: KeyCode<char>) {
|
||||
if !self.search_last_hightlight.is_empty() {
|
||||
self.rows[self.search_last_line].highlight = self.search_last_hightlight.clone();
|
||||
self.search_last_hightlight.clear();
|
||||
@ -1325,9 +1375,11 @@ impl Editor {
|
||||
|
||||
// Highlight matching search result
|
||||
let len = start + query.len();
|
||||
for x in start..len {
|
||||
self.rows[current as usize].highlight[x] = Highlight::SearchMatch;
|
||||
}
|
||||
highlight_range(
|
||||
&mut self.rows[current as usize].highlight,
|
||||
start..len,
|
||||
Highlight::SearchMatch,
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1359,97 +1411,103 @@ impl Editor {
|
||||
// Functions
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
fn get_syntax_db() -> Vec<EditorSyntax> {
|
||||
fn get_syntax_db() -> Vec<Syntax> {
|
||||
vec![
|
||||
EditorSyntax::new(
|
||||
Syntax::new(
|
||||
"c",
|
||||
vec![".c", ".h", ".cpp"],
|
||||
vec![
|
||||
"switch", "if", "while", "for", "break", "continue", "return", "else", "struct",
|
||||
"union", "typedef", "static", "enum", "class", "case",
|
||||
"continue", "typedef", "switch", "return", "static", "while", "break", "struct",
|
||||
"union", "class", "else", "enum", "for", "case", "if",
|
||||
],
|
||||
vec![
|
||||
"unsigned", "double", "signed", "float", "long", "char", "int", "void",
|
||||
],
|
||||
vec!["int", "long", "double", "float", "char", "unsigned", "signed", "void"],
|
||||
"//",
|
||||
"/*",
|
||||
"*/",
|
||||
EditorSyntaxFlags::HIGHLIGHT_NUMBERS | EditorSyntaxFlags::HIGHLIGHT_STRINGS,
|
||||
SyntaxFlags::HIGHLIGHT_NUMBERS | SyntaxFlags::HIGHLIGHT_STRINGS,
|
||||
),
|
||||
EditorSyntax::new(
|
||||
Syntax::new(
|
||||
"rust",
|
||||
vec![".rs"],
|
||||
vec![
|
||||
"as", "break", "const", "continue", "crate", "else", "enum", "extern", "false",
|
||||
"fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut",
|
||||
"pub", "ref", "return", "self", "static", "struct", "super", "trait", "true",
|
||||
"type", "unsafe", "use", "where", "while",
|
||||
"continue", "return", "static", "struct", "unsafe", "break", "const", "crate",
|
||||
"extern", "match", "super", "trait", "where", "else", "enum", "false", "impl",
|
||||
"loop", "move", "self", "type", "while", "for", "let", "mod", "pub", "ref", "true",
|
||||
"use", "mut", "as", "fn", "if", "in",
|
||||
],
|
||||
vec![
|
||||
"dyn",
|
||||
"Self",
|
||||
"Copy",
|
||||
"Send",
|
||||
"Sync",
|
||||
"Sized",
|
||||
"Sync",
|
||||
"Unpin",
|
||||
"Drop",
|
||||
"Fn",
|
||||
"FnMut",
|
||||
"FnOnce",
|
||||
"Box",
|
||||
"ToOwned",
|
||||
"Clone",
|
||||
"PartialEq",
|
||||
"PartialOrd",
|
||||
"Eq",
|
||||
"Ord",
|
||||
"AsRef",
|
||||
"AsMut",
|
||||
"Into",
|
||||
"From",
|
||||
"Default",
|
||||
"Iterator",
|
||||
"Extend",
|
||||
"IntoIterator",
|
||||
"DoubleEndedIterator",
|
||||
"ExactSizeIterator",
|
||||
"Option",
|
||||
"Some",
|
||||
"None",
|
||||
"Ok",
|
||||
"Err",
|
||||
"String",
|
||||
"IntoIterator",
|
||||
"PartialOrd",
|
||||
"PartialEq",
|
||||
"Iterator",
|
||||
"ToString",
|
||||
"Vec",
|
||||
"str",
|
||||
"char",
|
||||
"u8",
|
||||
"i8",
|
||||
"u16",
|
||||
"i16",
|
||||
"u32",
|
||||
"i32",
|
||||
"usize",
|
||||
"isize",
|
||||
"u64",
|
||||
"i64",
|
||||
"u128",
|
||||
"i128",
|
||||
"bool",
|
||||
"Default",
|
||||
"ToOwned",
|
||||
"Extend",
|
||||
"FnOnce",
|
||||
"Option",
|
||||
"String",
|
||||
"AsMut",
|
||||
"AsRef",
|
||||
"Clone",
|
||||
"Debug",
|
||||
"FnMut",
|
||||
"Sized",
|
||||
"Unpin",
|
||||
"array",
|
||||
"isize",
|
||||
"usize",
|
||||
"&str",
|
||||
"Copy",
|
||||
"Drop",
|
||||
"From",
|
||||
"Into",
|
||||
"None",
|
||||
"Self",
|
||||
"Send",
|
||||
"Some",
|
||||
"Sync",
|
||||
"Sync",
|
||||
"bool",
|
||||
"char",
|
||||
"i128",
|
||||
"u128",
|
||||
"Box",
|
||||
"Err",
|
||||
"Ord",
|
||||
"Vec",
|
||||
"dyn",
|
||||
"f32",
|
||||
"f64"
|
||||
"f64",
|
||||
"i16",
|
||||
"i32",
|
||||
"i64",
|
||||
"str",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"Eq",
|
||||
"Fn",
|
||||
"Ok",
|
||||
"i8",
|
||||
"u8",
|
||||
],
|
||||
"//",
|
||||
"/*",
|
||||
"*/",
|
||||
EditorSyntaxFlags::HIGHLIGHT_NUMBERS | EditorSyntaxFlags::HIGHLIGHT_STRINGS,
|
||||
SyntaxFlags::HIGHLIGHT_NUMBERS | SyntaxFlags::HIGHLIGHT_STRINGS,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Determine whether a character is one which separates tokens
|
||||
/// in the language to highlight
|
||||
fn is_separator(input_char: char) -> bool {
|
||||
if input_char.is_ascii_whitespace() || input_char == '\0' {
|
||||
if input_char.is_ascii_whitespace() || input_char == '\0' {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1464,6 +1522,27 @@ fn is_separator(input_char: char) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Get a range for a slice of a string or vector.
|
||||
///
|
||||
/// If `start` to `start + search_len`, is within the size of the search target (`haystack_len`)
|
||||
/// that range is returned. Otherwise, the range is from `start` to `haystack_len`.
|
||||
fn get_slice_range(start: usize, needle_len: usize, haystack_len: usize) -> Range<usize> {
|
||||
let search_len = start + needle_len;
|
||||
if search_len >= haystack_len {
|
||||
start..haystack_len
|
||||
} else {
|
||||
start..search_len
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the highlighting type for the specified range
|
||||
/// Kind of similar to the C memset calls
|
||||
fn highlight_range(vec: &mut Vec<Highlight>, range: Range<usize>, value: Highlight) {
|
||||
for x in range {
|
||||
vec[x] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
Loading…
Reference in New Issue
Block a user