refactor message parsing

macos-click-through
keith 5 years ago
parent 1f4dab9d9a
commit cb0d118859

@ -1,8 +1,9 @@
use std::collections::HashMap;
use neovim_lib::{Neovim, NeovimApi};
use skulpin::skia_safe::{colors, Color4f};
use neovim_lib::{Neovim, NeovimApi};
use crate::events::{GridLineCell, RedrawEvent};
#[derive(new, PartialEq, Debug, Clone)]
pub struct Colors {
@ -30,22 +31,13 @@ pub struct Style {
pub blend: u8
}
#[derive(new)]
pub struct GridLineCell {
pub grid: usize,
pub text: String,
pub row: usize,
pub col_start: usize,
pub style_id: Option<u64>
}
pub type GridCell = Option<(char, Style)>;
#[derive(new, Debug, Clone)]
pub struct DrawCommand {
pub text: String,
pub row: usize,
pub col_start: usize,
pub row: u64,
pub col_start: u64,
pub style: Style
}
@ -59,16 +51,16 @@ pub enum CursorType {
pub struct Editor {
pub nvim: Neovim,
pub grid: Vec<Vec<GridCell>>,
pub cursor_pos: (usize, usize),
pub cursor_pos: (u64, u64),
pub cursor_type: CursorType,
pub size: (usize, usize),
pub size: (u64, u64),
pub default_colors: Colors,
pub defined_styles: HashMap<u64, Style>,
pub previous_style: Option<Style>
}
impl Editor {
pub fn new(nvim: Neovim, width: usize, height: usize) -> Editor {
pub fn new(nvim: Neovim, width: u64, height: u64) -> Editor {
let mut editor = Editor {
nvim,
grid: Vec::new(),
@ -101,7 +93,7 @@ impl Editor {
}
}
fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: usize, col_index: usize, style: Style) {
fn add_character(command: &mut Option<DrawCommand>, character: &char, row_index: u64, col_index: u64, style: Style) {
match command {
Some(command) => command.text.push(character.clone()),
None => {
@ -116,7 +108,7 @@ impl Editor {
add_command(&mut draw_commands, command);
command = None;
}
add_character(&mut command, &character, row_index as usize, col_index as usize, new_style.clone());
add_character(&mut command, &character, row_index as u64, col_index as u64, new_style.clone());
} else {
add_command(&mut draw_commands, command);
command = None;
@ -128,47 +120,91 @@ impl Editor {
}).flatten().collect()
}
pub fn draw(&mut self, command: GridLineCell) {
let row_index = command.row as usize;
let col_start = command.col_start as usize;
pub fn handle_redraw_event(&mut self, event: RedrawEvent) {
match event {
RedrawEvent::Resize { width, height, .. } => self.resize(width, height),
RedrawEvent::DefaultColorsSet { foreground, background, special } => self.set_default_colors(foreground, background, special),
RedrawEvent::HighlightAttributesDefine { id, style } => self.define_style(id, style),
RedrawEvent::GridLine { row, column_start, cells, .. } => self.draw_grid_line(row, column_start, cells),
RedrawEvent::Clear { .. } => self.clear(),
RedrawEvent::CursorGoto { row, column, .. } => self.jump_cursor_to(row, column),
RedrawEvent::Scroll { top, bottom, left, right, rows, columns, .. } => self.scroll_region(top, bottom, left, right, rows, columns)
}
}
pub fn resize(&mut self, new_width: u64, new_height: u64) {
self.nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed");
self.size = (new_width, new_height);
}
fn set_default_colors(&mut self, foreground: Color4f, background: Color4f, special: Color4f) {
self.default_colors = Colors::new(Some(foreground), Some(background), Some(special));
}
fn define_style(&mut self, id: u64, style: Style) {
self.defined_styles.insert(id, style);
}
let style = match (command.style_id, self.previous_style.clone()) {
fn draw_grid_line_cell(&mut self, row_index: u64, column_pos: &mut u64, cell: GridLineCell) {
let style = match (cell.highlight_id, self.previous_style.clone()) {
(Some(0), _) => Style::new(self.default_colors.clone()),
(Some(style_id), _) => self.defined_styles.get(&style_id).expect("GridLineCell must use defined color").clone(),
(None, Some(previous_style)) => previous_style,
(None, None) => Style::new(self.default_colors.clone())
};
if row_index < self.grid.len() {
let row = self.grid.get_mut(row_index).expect("Grid must have size greater than row_index");
for (i, character) in command.text.chars().enumerate() {
let pointer_index = i + col_start;
if pointer_index < row.len() {
row[pointer_index] = Some((character, style.clone()));
}
let mut text = cell.text;
if let Some(times) = cell.repeat {
text = text.repeat(times as usize);
}
let row = self.grid.get_mut(row_index as usize).expect("Grid must have size greater than row_index");
for (i, character) in text.chars().enumerate() {
let pointer_index = i + *column_pos as usize;
if pointer_index < row.len() {
row[pointer_index] = Some((character, style.clone()));
}
}
*column_pos = *column_pos + text.chars().count() as u64;
self.previous_style = Some(style);
}
fn draw_grid_line(&mut self, row: u64, column_start: u64, cells: Vec<GridLineCell>) {
if row < self.grid.len() as u64 {
let mut column_pos = column_start;
for cell in cells {
self.draw_grid_line_cell(row, &mut column_pos, cell);
}
} else {
println!("Draw command out of bounds");
}
}
self.previous_style = Some(style);
fn clear(&mut self) {
let (width, height) = self.size;
self.grid = vec![vec![None; width as usize]; height as usize];
}
fn jump_cursor_to(&mut self, row: u64, col: u64) {
self.cursor_pos = (row, col);
}
pub fn scroll_region(&mut self, top: isize, bot: isize, left: isize, right: isize, rows: isize, cols: isize) {
fn scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64, rows: i64, cols: i64) {
let (top, bot) = if rows > 0 {
(top + rows, bot)
(top as i64 + rows, bot as i64)
} else if rows < 0 {
(top, bot + rows)
(top as i64, bot as i64 + rows)
} else {
(top, bot)
(top as i64, bot as i64)
};
let (left, right) = if cols > 0 {
(left + cols, right)
(left as i64 + cols, right as i64)
} else if rows < 0 {
(left, right + cols)
(left as i64, right as i64 + cols)
} else {
(left, right)
(left as i64, right as i64)
};
let width = right - left;
@ -184,45 +220,22 @@ impl Editor {
region.push(copied_section);
}
let new_top = top as isize - rows;
let new_left = left as isize - cols;
let new_top = top as i64 - rows;
let new_left = left as i64 - cols;
dbg!(top, bot, left, right, rows, cols, new_top, new_left);
for (y, row_section) in region.into_iter().enumerate() {
for (x, cell) in row_section.into_iter().enumerate() {
let y = new_top + y as isize;
if y >= 0 && y < self.grid.len() as isize {
let y = new_top + y as i64;
if y >= 0 && y < self.grid.len() as i64 {
let mut row = &mut self.grid[y as usize];
let x = new_left + x as isize;
if x >= 0 && x < row.len() as isize {
let x = new_left + x as i64;
if x >= 0 && x < row.len() as i64 {
row[x as usize] = cell;
}
}
}
}
}
pub fn clear(&mut self) {
let (width, height) = self.size;
self.grid = vec![vec![None; width as usize]; height as usize];
}
pub fn resize(&mut self, new_width: usize, new_height: usize) {
self.nvim.ui_try_resize(new_width as i64, new_height as i64).expect("Resize failed");
self.size = (new_width, new_height);
}
pub fn define_style(&mut self, id: u64, style: Style) {
self.defined_styles.insert(id, style);
}
pub fn set_default_colors(&mut self, foreground: Color4f, background: Color4f, special: Color4f) {
self.default_colors = Colors::new(Some(foreground), Some(background), Some(special));
}
pub fn jump_cursor_to(&mut self, row: usize, col: usize) {
self.cursor_pos = (row, col);
}
}

@ -1,19 +1,55 @@
use std::error;
use std::fmt;
use rmpv::Value;
use skulpin::skia_safe::Color4f;
use crate::editor::{Colors, Style};
#[derive(Debug, Clone)]
pub enum EventParseError {
InvalidArray(Value),
InvalidString(Value),
InvalidU64(Value),
InvalidI64(Value),
InvalidEventFormat
}
type Result<T> = std::result::Result<T, EventParseError>;
impl fmt::Display for EventParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EventParseError::InvalidArray(value) => write!(f, "invalid array format {}", value),
EventParseError::InvalidString(value) => write!(f, "invalid string format {}", value),
EventParseError::InvalidU64(value) => write!(f, "invalid u64 format {}", value),
EventParseError::InvalidI64(value) => write!(f, "invalid i64 format {}", value),
EventParseError::InvalidEventFormat => write!(f, "invalid event format")
}
}
}
impl error::Error for EventParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub struct GridLineCell {
text: String,
highlight_id: Option<usize>,
repeat: Option<usize>
pub text: String,
pub highlight_id: Option<u64>,
pub repeat: Option<u64>
}
#[derive(Debug)]
pub enum RedrawEvent {
Resize { grid: usize, width: usize, height: usize },
Resize { grid: u64, width: u64, height: u64 },
DefaultColorsSet { foreground: Color4f, background: Color4f, special: Color4f },
HighlightAttributesDefine { id: usize, style: Style },
GridLine { grid: usize, row: usize, column_start: usize, cells: Vec<GridLineCell> },
Clear { grid: usize },
CursorGoto { grid: usize, row: usize, column: usize },
Scroll { grid: usize, top: usize, bottom: usize, left: usize, right: usize, rows: isize, cols: isize }
HighlightAttributesDefine { id: u64, style: Style },
GridLine { grid: u64, row: u64, column_start: u64, cells: Vec<GridLineCell> },
Clear { grid: u64 },
CursorGoto { grid: u64, row: u64, column: u64 },
Scroll { grid: u64, top: u64, bottom: u64, left: u64, right: u64, rows: i64, columns: i64 }
}
fn unpack_color(packed_color: u64) -> Color4f {
@ -29,4 +65,174 @@ fn unpack_color(packed_color: u64) -> Color4f {
}
}
pub fn parse_neovim_event(event_name:
fn parse_array(array_value: &Value) -> Result<Vec<Value>> {
if let Value::Array(content) = array_value.clone() {
Ok(content.to_vec())
} else {
Err(EventParseError::InvalidArray(array_value.clone()))
}
}
fn parse_string(string_value: &Value) -> Result<String> {
if let Value::String(content) = string_value.clone() {
Ok(content.into_str().ok_or(EventParseError::InvalidString(string_value.clone()))?)
} else {
Err(EventParseError::InvalidString(string_value.clone()))
}
}
fn parse_u64(u64_value: &Value) -> Result<u64> {
if let Value::Integer(content) = u64_value.clone() {
Ok(content.as_u64().ok_or(EventParseError::InvalidU64(u64_value.clone()))?)
} else {
Err(EventParseError::InvalidU64(u64_value.clone()))
}
}
fn parse_i64(i64_value: &Value) -> Result<i64> {
if let Value::Integer(content) = i64_value.clone() {
Ok(content.as_i64().ok_or(EventParseError::InvalidI64(i64_value.clone()))?)
} else {
Err(EventParseError::InvalidI64(i64_value.clone()))
}
}
fn parse_default_colors(default_colors_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [
foreground, background, special, _term_foreground, _term_background
] = default_colors_arguments.as_slice() {
Ok(RedrawEvent::DefaultColorsSet {
foreground: unpack_color(parse_u64(&foreground)?),
background: unpack_color(parse_u64(&background)?),
special: unpack_color(parse_u64(special)?),
})
} else {
Err(EventParseError::InvalidEventFormat)
}
}
fn parse_hl_attr_define(hl_attr_define_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [
id, Value::Map(attributes), _terminal_attributes, _info
] = hl_attr_define_arguments.as_slice() {
let mut style = Style::new(Colors::new(None, None, None));
for attribute in attributes {
if let (Value::String(name), value) = attribute {
match (name.as_str().unwrap(), value) {
("foreground", Value::Integer(packed_color)) => style.colors.foreground = Some(unpack_color(packed_color.as_u64().unwrap())),
("background", Value::Integer(packed_color)) => style.colors.background = Some(unpack_color(packed_color.as_u64().unwrap())),
("special", Value::Integer(packed_color)) => style.colors.special = Some(unpack_color(packed_color.as_u64().unwrap())),
_ => println!("Ignored style attribute: {}", name)
}
} else {
println!("Invalid attribute format");
}
}
Ok(RedrawEvent::HighlightAttributesDefine { id: parse_u64(&id)?, style })
} else {
Err(EventParseError::InvalidEventFormat)
}
}
fn parse_grid_line_cell(grid_line_cell: Value) -> Result<GridLineCell> {
let cell_contents = parse_array(&grid_line_cell)?;
let text_value = cell_contents.get(0).ok_or(EventParseError::InvalidEventFormat)?;
Ok(GridLineCell {
text: parse_string(&text_value)?,
highlight_id: cell_contents.get(1).map(|highlight_id| parse_u64(highlight_id)).transpose()?,
repeat: cell_contents.get(2).map(|repeat| parse_u64(repeat)).transpose()?
})
}
fn parse_grid_line(grid_line_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [grid_id, row, column_start, cells] = grid_line_arguments.as_slice() {
Ok(RedrawEvent::GridLine {
grid: parse_u64(&grid_id)?,
row: parse_u64(&row)?, column_start: parse_u64(&column_start)?,
cells: parse_array(&cells)?
.into_iter()
.map(parse_grid_line_cell)
.collect::<Result<Vec<GridLineCell>>>()?
})
} else {
Err(EventParseError::InvalidEventFormat)
}
}
fn parse_clear(clear_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [grid_id] = clear_arguments.as_slice() {
Ok(RedrawEvent::Clear { grid: parse_u64(&grid_id)? })
} else {
Err(EventParseError::InvalidEventFormat)
}
}
fn parse_cursor_goto(cursor_goto_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [grid_id, column, row] = cursor_goto_arguments.as_slice() {
Ok(RedrawEvent::CursorGoto {
grid: parse_u64(&grid_id)?, row: parse_u64(&row)?, column: parse_u64(&column)?
})
} else {
Err(EventParseError::InvalidEventFormat)
}
}
fn parse_grid_scroll(grid_scroll_arguments: Vec<Value>) -> Result<RedrawEvent> {
if let [grid_id, top, bottom, left, right, rows, columns] = grid_scroll_arguments.as_slice() {
Ok(RedrawEvent::Scroll {
grid: parse_u64(&grid_id)?,
top: parse_u64(&top)?, bottom: parse_u64(&bottom)?,
left: parse_u64(&left)?, right: parse_u64(&right)?,
rows: parse_i64(&rows)?, columns: parse_i64(&columns)?
})
} else {
Err(EventParseError::InvalidEventFormat)
}
}
pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
let mut event_contents = parse_array(&event_value)?.to_vec();
let name_value = event_contents.get(0).ok_or(EventParseError::InvalidEventFormat)?;
let event_name = parse_string(&name_value)?;
let events = event_contents;
let mut parsed_events = Vec::new();
for event in &events[1..] {
let event_parameters = parse_array(&event)?;
let possible_parsed_event = match event_name.clone().as_ref() {
"default_colors_set" => Some(parse_default_colors(event_parameters)?),
"hl_attr_define" => Some(parse_hl_attr_define(event_parameters)?),
"grid_line" => Some(parse_grid_line(event_parameters)?),
"grid_clear" => Some(parse_clear(event_parameters)?),
"grid_cursor_goto" => Some(parse_cursor_goto(event_parameters)?),
"grid_scroll" => Some(parse_grid_scroll(event_parameters)?),
_ => None
};
if let Some(parsed_event) = possible_parsed_event {
parsed_events.push(parsed_event);
} else {
println!("Did not parse {}", event_name);
}
}
Ok(parsed_events)
}
pub fn parse_neovim_event(event_name: String, events: Vec<Value>) -> Result<Vec<RedrawEvent>> {
let mut resulting_events = Vec::new();
if event_name == "redraw" {
for event in events {
resulting_events.append(&mut parse_redraw_event(event)?);
}
} else {
println!("Unknown global event {}", event_name);
}
Ok(resulting_events)
}

@ -1,6 +1,7 @@
// #![windows_subsystem = "windows"]
mod editor;
mod events;
mod window;
mod keybindings;
@ -17,160 +18,22 @@ use neovim_lib::{Neovim, UiAttachOptions, Session};
use rmpv::Value;
use window::ui_loop;
use editor::{Colors, Editor, GridLineCell, Style};
use skulpin::skia_safe::Color4f;
const INITIAL_WIDTH: usize = 100;
const INITIAL_HEIGHT: usize = 50;
fn handle_grid_line(grid_line_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [Value::Integer(grid_id), Value::Integer(row), Value::Integer(col_start), Value::Array(cells)] = grid_line_arguments.as_slice() {
let mut col_pos = col_start.as_u64().unwrap() as usize;
for cell in cells.into_iter() {
if let Value::Array(cell_data) = cell {
let grid_id = grid_id.as_u64().unwrap() as usize;
let row = row.as_u64().unwrap() as usize;
let mut text = match cell_data.get(0).expect("Cell must have non zero size") {
Value::String(cell_text) => cell_text.as_str().expect("Could not process string").to_string(),
_ => panic!("Cell text was not a string")
};
if let Some(Value::Integer(repeat_count)) = cell_data.get(2) {
text = text.repeat(repeat_count.as_u64().unwrap_or(1) as usize);
}
let mut style_id = None;
if let Some(Value::Integer(id)) = cell_data.get(1) {
style_id = Some(id.as_u64().unwrap());
}
let mut editor = editor.lock().unwrap();
let length = text.chars().count();
editor.draw(GridLineCell::new(grid_id, text, row, col_pos, style_id));
col_pos = col_pos + length;
} else {
println!("Invalid grid_line cell format: {:?}", cell);
}
}
} else {
println!("Invalid grid_line format: {:?}", grid_line_arguments);
}
}
use editor::Editor;
use events::parse_neovim_event;
fn handle_clear(_clear_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
let mut editor = editor.lock().unwrap();
editor.clear();
}
const INITIAL_WIDTH: u64 = 100;
const INITIAL_HEIGHT: u64 = 50;
fn handle_cursor_goto(cursor_goto_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [Value::Integer(_grid_id), Value::Integer(row), Value::Integer(column)] = cursor_goto_arguments.as_slice() {
let mut editor = editor.lock().unwrap();
editor.jump_cursor_to(column.as_u64().unwrap() as usize, row.as_u64().unwrap() as usize);
} else {
println!("Invalid cursor_goto format: {:?}", cursor_goto_arguments);
}
}
fn handle_default_colors(default_colors_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [
Value::Integer(foreground), Value::Integer(background), Value::Integer(special),
Value::Integer(_term_foreground), Value::Integer(_term_background)
] = default_colors_arguments.as_slice() {
let foreground = unpack_color(foreground.as_u64().unwrap());
let background = unpack_color(background.as_u64().unwrap());
let special = unpack_color(special.as_u64().unwrap());
let mut editor = editor.lock().unwrap();
editor.set_default_colors(foreground, background, special);
} else {
println!("Invalid default color format.");
}
}
fn handle_hl_attr_define(hl_attr_define_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [
Value::Integer(id), Value::Map(attributes), Value::Map(_terminal_attributes), Value::Array(_info)
] = hl_attr_define_arguments.as_slice() {
let id = id.as_u64().unwrap();
let mut style = Style::new(Colors::new(None, None, None));
for attribute in attributes {
if let (Value::String(name), value) = attribute {
match (name.as_str().unwrap(), value) {
("foreground", Value::Integer(packed_color)) => style.colors.foreground = Some(unpack_color(packed_color.as_u64().unwrap())),
("background", Value::Integer(packed_color)) => style.colors.background = Some(unpack_color(packed_color.as_u64().unwrap())),
("special", Value::Integer(packed_color)) => style.colors.special = Some(unpack_color(packed_color.as_u64().unwrap())),
_ => println!("Ignored style attribute: {}", name)
}
} else {
println!("Invalid attribute format");
}
}
let mut editor = editor.lock().unwrap();
editor.define_style(id, style);
}
}
fn handle_grid_scroll(grid_scroll_arguments: &Vec<Value>, editor: &Arc<Mutex<Editor>>) {
if let [
Value::Integer(_grid_id), Value::Integer(top), Value::Integer(bot), Value::Integer(left),
Value::Integer(right), Value::Integer(rows), Value::Integer(cols)
] = grid_scroll_arguments.as_slice() {
let top = top.as_u64().unwrap() as isize;
let bot = bot.as_u64().unwrap() as isize;
let left = left.as_u64().unwrap() as isize;
let right = right.as_u64().unwrap() as isize;
let rows = rows.as_i64().unwrap() as isize;
let cols = cols.as_i64().unwrap() as isize;
let mut editor = editor.lock().unwrap();
editor.scroll_region(top, bot, left, right, rows, cols);
}
}
fn handle_redraw_event(event_value: Value, editor: &Arc<Mutex<Editor>>) {
match event_value {
Value::Array(event_contents) => {
let name_value = &event_contents[0];
let events = &event_contents[1..];
for event in events {
match (name_value, event) {
(Value::String(event_name), Value::Array(arguments)) => {
match event_name.as_str().expect("Invalid redraw command name format.") {
"grid_resize" => println!("grid_resize event ignored"),
"default_colors_set" => handle_default_colors(arguments, editor),
"hl_attr_define" => handle_hl_attr_define(arguments, editor),
"hl_group_set" => println!("hl_group_set event ignored"),
"grid_line" => handle_grid_line(arguments, &editor),
"grid_clear" => handle_clear(arguments, &editor),
"grid_cursor_goto" => handle_cursor_goto(arguments, &editor),
"grid_scroll" => handle_grid_scroll(arguments, &editor),
other => println!("Unhandled redraw command {}", other)
}
},
_ => {
println!("Unrecognized redraw event structure.");
}
}
}
},
_ => println!("Event is not an array...")
}
}
fn nvim_event_loop(receiver: Receiver<(String, Vec<Value>)>, editor: &Arc<Mutex<Editor>>) {
println!("UI thread spawned");
loop {
let (event_name, event_args) = receiver.recv().expect("Could not receive event.");
match event_name.as_ref() {
"redraw" => {
for event in event_args {
handle_redraw_event(event, &editor);
}
},
_ => println!("Unrecognized Event: {}", event_name)
};
let (event_name, events) = receiver.recv().expect("Could not receive event.");
let parsed_events = parse_neovim_event(event_name, events).expect("Event parse failed...");
for event in parsed_events {
let mut editor = editor.lock().unwrap();
editor.handle_redraw_event(event);
}
}
}

@ -85,7 +85,7 @@ fn draw(
if let CursorType::Block = cursor_type {
let text_paint = Paint::new(default_colors.background.unwrap(), None);
let editor = editor.lock().unwrap();
let character = editor.grid[cursor_grid_y][cursor_grid_x].clone()
let character = editor.grid[cursor_grid_y as usize][cursor_grid_x as usize].clone()
.map(|(character, _)| character)
.unwrap_or(' ');
let text_y = cursor_y + font_height - font_height * 0.2;
@ -140,8 +140,8 @@ pub fn ui_loop(editor: Arc<Mutex<Editor>>) {
} => {
if new_size.width > 0.0 && new_size.height > 0.0 {
editor.lock().unwrap().resize(
(new_size.width as f32 / font_width) as usize,
(new_size.height as f32 / font_height) as usize
(new_size.width as f32 / font_width) as u64,
(new_size.height as f32 / font_height) as u64
)
}
},

Loading…
Cancel
Save