Create language syntax struct, and select highlighting by file extension
This commit is contained in:
parent
13eb9fd7b4
commit
f25252a39e
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -36,6 +36,7 @@ dependencies = [
|
||||
name = "rs-kilo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.1.0"
|
||||
libc = "0.2"
|
||||
# Rust wrappers for C/POSIX headers
|
||||
nix = "0.15.0"
|
||||
|
158
src/editor.rs
158
src/editor.rs
@ -10,9 +10,44 @@ use std::time::{Duration, Instant};
|
||||
|
||||
use self::EditorKey::*;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Defines
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
const KILO_TAB_STOP: usize = 4;
|
||||
const KILO_QUIT_TIMES: u8 = 3;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Data
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Use an external package's macro to create a memory-safe
|
||||
// bit flag alternative
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct EditorSyntaxFlags: u32 {
|
||||
const HIGHLIGHT_NUMBERS = 0b00000001;
|
||||
const HIGHLIGHT_STRINGS = 0b00000010;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct EditorSyntax {
|
||||
file_type: String,
|
||||
file_match: Vec<&'static str>,
|
||||
flags: EditorSyntaxFlags,
|
||||
}
|
||||
|
||||
impl EditorSyntax {
|
||||
pub fn new(file_type: &str, file_match: Vec<&'static str>, flags: EditorSyntaxFlags) -> Self {
|
||||
EditorSyntax {
|
||||
file_type: String::from(file_type),
|
||||
file_match,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Highlight {
|
||||
Normal,
|
||||
@ -53,6 +88,7 @@ pub struct Editor {
|
||||
filename: String,
|
||||
status_message: String,
|
||||
status_message_time: Instant,
|
||||
syntax: Option<EditorSyntax>,
|
||||
|
||||
// Properties not present in C version
|
||||
output_buffer: String,
|
||||
@ -108,6 +144,7 @@ impl Default for Editor {
|
||||
filename: String::new(),
|
||||
status_message: String::new(),
|
||||
status_message_time: Instant::now(),
|
||||
syntax: None,
|
||||
|
||||
output_buffer: String::new(),
|
||||
quit_times: KILO_QUIT_TIMES,
|
||||
@ -310,22 +347,19 @@ impl Editor {
|
||||
// Syntax Highlighting
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
fn is_separator(c: char) -> bool {
|
||||
let separator_chars = ",.()+-/*=~%<>[];";
|
||||
|
||||
for ch in separator_chars.chars() {
|
||||
if c == ch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
c.is_ascii_whitespace() || c == '\0'
|
||||
}
|
||||
|
||||
fn update_syntax(&mut self, index: usize) {
|
||||
let row = &mut self.rows[index];
|
||||
row.highlight = vec![Highlight::Normal; row.render.len()];
|
||||
|
||||
if self.syntax.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is dumb. This lets you get a reference to the item in
|
||||
// the option, by turning Option<T> into Option<&T>,
|
||||
// which can then be unwrapped.
|
||||
let current_syntax = self.syntax.as_ref().unwrap();
|
||||
|
||||
let mut prev_separator = false;
|
||||
|
||||
let mut i = 0;
|
||||
@ -338,21 +372,26 @@ impl Editor {
|
||||
Highlight::Normal
|
||||
};
|
||||
|
||||
if (c.is_ascii_digit() && (prev_separator || prev_highlight == Highlight::Number))
|
||||
|| (c == '.' && prev_highlight == Highlight::Number)
|
||||
if current_syntax
|
||||
.flags
|
||||
.contains(EditorSyntaxFlags::HIGHLIGHT_NUMBERS)
|
||||
{
|
||||
row.highlight[i as usize] = Highlight::Number;
|
||||
i += 1;
|
||||
prev_separator = false;
|
||||
continue;
|
||||
if (c.is_ascii_digit() && (prev_separator || prev_highlight == Highlight::Number))
|
||||
|| (c == '.' && prev_highlight == Highlight::Number)
|
||||
{
|
||||
row.highlight[i as usize] = Highlight::Number;
|
||||
i += 1;
|
||||
prev_separator = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
prev_separator = Self::is_separator(c);
|
||||
prev_separator = is_separator(c);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax_to_color(&self, syntax_type: Highlight) -> i32 {
|
||||
fn syntax_to_color(syntax_type: Highlight) -> i32 {
|
||||
use Highlight::*;
|
||||
|
||||
match syntax_type {
|
||||
@ -362,6 +401,32 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn select_syntax_highlight(&mut self) {
|
||||
if self.filename.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut parts: Vec<&str> = self.filename.split('.').collect();
|
||||
|
||||
let file_ext = String::from(".")
|
||||
+ match parts.pop() {
|
||||
Some(ext) => ext,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let languages = &get_syntax_db();
|
||||
|
||||
for language in languages {
|
||||
let file_match = language.file_match.clone();
|
||||
for ext in file_match {
|
||||
if ext == file_ext {
|
||||
self.syntax = Some(language.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Input
|
||||
// ------------------------------------------------------------------------
|
||||
@ -654,7 +719,7 @@ impl Editor {
|
||||
}
|
||||
self.append_out_char(ch);
|
||||
} else {
|
||||
let color = self.syntax_to_color(self.rows[file_row].highlight[x]);
|
||||
let color = Self::syntax_to_color(self.rows[file_row].highlight[x]);
|
||||
if color != current_color {
|
||||
current_color = color;
|
||||
let code = format!("\x1b[{}m", color);
|
||||
@ -683,7 +748,11 @@ impl Editor {
|
||||
let modified = if self.dirty > 0 { "(modified}" } else { "" };
|
||||
|
||||
let mut left_message = format!("{:.80} - {} lines {}", filename, self.rows.len(), modified);
|
||||
let right_message = format!("{}/{}", self.cursor_y + 1, self.rows.len());
|
||||
let file_type = match &self.syntax {
|
||||
Some(s) => &s.file_type,
|
||||
None => "no ft",
|
||||
};
|
||||
let right_message = format!("{} | {}/{}", file_type, self.cursor_y + 1, self.rows.len());
|
||||
let mut len = left_message.len();
|
||||
if len > self.screen_cols {
|
||||
len = self.screen_cols;
|
||||
@ -959,6 +1028,9 @@ impl Editor {
|
||||
/// Open a file for display
|
||||
pub fn open(&mut self, filename: &str) -> io::Result<()> {
|
||||
self.filename = filename.to_owned();
|
||||
|
||||
self.select_syntax_highlight();
|
||||
|
||||
let file = File::open(&self.filename)?;
|
||||
let buf_reader = BufReader::new(file);
|
||||
|
||||
@ -980,6 +1052,7 @@ impl Editor {
|
||||
self.set_status_message("Save aborted");
|
||||
return Ok(());
|
||||
}
|
||||
self.select_syntax_highlight();
|
||||
}
|
||||
|
||||
let mut file = File::create(&self.filename)?;
|
||||
@ -1084,3 +1157,44 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Functions
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
fn get_syntax_db() -> Vec<EditorSyntax> {
|
||||
vec![EditorSyntax::new(
|
||||
"c",
|
||||
vec![".c", ".h", ".cpp"],
|
||||
EditorSyntaxFlags::HIGHLIGHT_NUMBERS,
|
||||
)]
|
||||
}
|
||||
|
||||
fn is_separator(c: char) -> bool {
|
||||
let separator_chars = ",.()+-/*=~%<>[];";
|
||||
|
||||
for ch in separator_chars.chars() {
|
||||
if c == ch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
c.is_ascii_whitespace() || c == '\0'
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn select_syntax_hightlight_selects_language() {
|
||||
let langs = get_syntax_db();
|
||||
let mut editor = Editor::new();
|
||||
editor.filename = String::from("foo.c");
|
||||
|
||||
editor.select_syntax_highlight();
|
||||
|
||||
assert_eq!(editor.syntax.as_ref(), Some(&langs[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,8 +65,6 @@ pub fn enable_raw_mode() {
|
||||
raw.local_flags
|
||||
.remove(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
|
||||
|
||||
// raw.control_flags.remove(ControlFlags::CSIZE | ControlFlags::PARENB);
|
||||
|
||||
raw.control_chars[SpecialCharacterIndices::VMIN as usize] = 0;
|
||||
raw.control_chars[SpecialCharacterIndices::VTIME as usize] = 1;
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
mod editor;
|
||||
mod helpers;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user