fix #2
This commit is contained in:
@@ -11,6 +11,7 @@ use ratatui::{
|
|||||||
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
calc::{Grid, LEN},
|
calc::{Grid, LEN},
|
||||||
|
error_msg::ErrorMessage,
|
||||||
mode::Mode,
|
mode::Mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ pub struct App {
|
|||||||
pub grid: Grid,
|
pub grid: Grid,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub file: Option<PathBuf>,
|
pub file: Option<PathBuf>,
|
||||||
|
pub error_msg: ErrorMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &App {
|
impl Widget for &App {
|
||||||
@@ -75,14 +77,13 @@ impl Widget for &App {
|
|||||||
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
||||||
|
|
||||||
match (x == 0, y == 0) {
|
match (x == 0, y == 0) {
|
||||||
|
// 0,0 dead space
|
||||||
(true, true) => {
|
(true, true) => {
|
||||||
let (x, y) = self.grid.selected_cell;
|
display = self.mode.to_string();
|
||||||
let c = Grid::num_to_char(x);
|
style = self.mode.get_style();
|
||||||
display = format!("{y}{c}",);
|
|
||||||
style = Style::new().fg(Color::Green).bg(Color::Black);
|
|
||||||
}
|
}
|
||||||
(true, false) => {
|
|
||||||
// row names
|
// row names
|
||||||
|
(true, false) => {
|
||||||
display = y_idx.to_string();
|
display = y_idx.to_string();
|
||||||
|
|
||||||
let bg = if y_idx == self.grid.selected_cell.1 {
|
let bg = if y_idx == self.grid.selected_cell.1 {
|
||||||
@@ -94,8 +95,8 @@ impl Widget for &App {
|
|||||||
};
|
};
|
||||||
style = Style::new().fg(Color::White).bg(bg);
|
style = Style::new().fg(Color::White).bg(bg);
|
||||||
}
|
}
|
||||||
(false, true) => {
|
|
||||||
// column names
|
// column names
|
||||||
|
(false, true) => {
|
||||||
display = Grid::num_to_char(x_idx);
|
display = Grid::num_to_char(x_idx);
|
||||||
|
|
||||||
let bg = if x_idx == self.grid.selected_cell.0 {
|
let bg = if x_idx == self.grid.selected_cell.0 {
|
||||||
@@ -108,19 +109,19 @@ impl Widget for &App {
|
|||||||
|
|
||||||
style = Style::new().fg(Color::White).bg(bg)
|
style = Style::new().fg(Color::White).bg(bg)
|
||||||
}
|
}
|
||||||
|
// grid squares
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
if let Some(cell) = self.grid.get_cell_raw(x_idx, y_idx) {
|
if let Some(cell) = self.grid.get_cell_raw(x_idx, y_idx) {
|
||||||
display = cell.as_raw_string();
|
match cell {
|
||||||
|
crate::app::calc::CellType::Number(c) => display = c.to_string(),
|
||||||
if cell.can_be_number() {
|
crate::app::calc::CellType::String(s) => display = s.to_owned(),
|
||||||
if let Some(val) = self.grid.evaluate(&cell.as_raw_string()) {
|
crate::app::calc::CellType::Equation(e) => {
|
||||||
|
if let Some(val) = self.grid.evaluate(e) {
|
||||||
display = val.to_string();
|
display = val.to_string();
|
||||||
style = Style::new()
|
style = Style::new()
|
||||||
.underline_color(Color::DarkGray)
|
.underline_color(Color::DarkGray)
|
||||||
.add_modifier(Modifier::UNDERLINED);
|
.add_modifier(Modifier::UNDERLINED);
|
||||||
} else {
|
} else {
|
||||||
// broken formulas
|
|
||||||
if cell.is_equation() {
|
|
||||||
style =
|
style =
|
||||||
Style::new().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED)
|
Style::new().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED)
|
||||||
}
|
}
|
||||||
@@ -152,6 +153,7 @@ impl App {
|
|||||||
grid: Grid::new(),
|
grid: Grid::new(),
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
file: None,
|
file: None,
|
||||||
|
error_msg: ErrorMessage::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +193,7 @@ impl App {
|
|||||||
Mode::Normal => frame.render_widget(
|
Mode::Normal => frame.render_widget(
|
||||||
Paragraph::new({
|
Paragraph::new({
|
||||||
let (x, y) = self.grid.selected_cell;
|
let (x, y) = self.grid.selected_cell;
|
||||||
let cell = self.grid.get_cell_raw(x, y).as_ref().map(|f| f.as_raw_string()).unwrap_or_default();
|
let cell = self.grid.get_cell_raw(x, y).as_ref().map(|f| f.to_string()).unwrap_or_default();
|
||||||
cell
|
cell
|
||||||
}),
|
}),
|
||||||
cmd_line_left,
|
cmd_line_left,
|
||||||
@@ -200,7 +202,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame.render_widget(self, body);
|
frame.render_widget(self, body);
|
||||||
frame.render_widget(&self.mode, cmd_line_right);
|
frame.render_widget(&self.error_msg, cmd_line_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> io::Result<()> {
|
fn handle_events(&mut self) -> io::Result<()> {
|
||||||
@@ -251,9 +253,9 @@ impl App {
|
|||||||
event::KeyCode::Char(c) => {
|
event::KeyCode::Char(c) => {
|
||||||
Mode::process_key(self, c);
|
Mode::process_key(self, c);
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => todo!(),
|
_ => {}
|
||||||
},
|
},
|
||||||
Mode::Visual(_start_pos) => {
|
Mode::Visual(_start_pos) => {
|
||||||
if let event::Event::Key(key) = event::read()? {
|
if let event::Event::Key(key) = event::read()? {
|
||||||
|
|||||||
188
src/app/calc.rs
188
src/app/calc.rs
@@ -2,10 +2,10 @@ use std::fmt::Display;
|
|||||||
|
|
||||||
use evalexpr::*;
|
use evalexpr::*;
|
||||||
|
|
||||||
use crate::ctx;
|
use crate::app::ctx;
|
||||||
|
|
||||||
// if this is very large at all it will overflow the stack
|
// if this is very large at all it will overflow the stack
|
||||||
pub const LEN: usize = 100;
|
pub const LEN: usize = 10;
|
||||||
|
|
||||||
pub struct Grid {
|
pub struct Grid {
|
||||||
// a b c ...
|
// a b c ...
|
||||||
@@ -13,7 +13,7 @@ pub struct Grid {
|
|||||||
// 1
|
// 1
|
||||||
// 2
|
// 2
|
||||||
// ...
|
// ...
|
||||||
cells: [[Option<Box<dyn Cell>>; LEN]; LEN],
|
cells: [[Option<CellType>; LEN]; LEN],
|
||||||
/// (X, Y)
|
/// (X, Y)
|
||||||
pub selected_cell: (usize, usize),
|
pub selected_cell: (usize, usize),
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ impl std::fmt::Debug for Grid {
|
|||||||
impl Grid {
|
impl Grid {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// TODO this needs to be moved to the heap
|
// TODO this needs to be moved to the heap
|
||||||
let b: [[Option<Box<dyn Cell>>; LEN]; LEN] =
|
let b: [[Option<CellType>; LEN]; LEN] =
|
||||||
core::array::from_fn(|_| core::array::from_fn(|_| None));
|
core::array::from_fn(|_| core::array::from_fn(|_| None));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -90,12 +90,12 @@ impl Grid {
|
|||||||
(x_idx, y_idx)
|
(x_idx, y_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cell<T: Into<Box<dyn Cell>>>(&mut self, cell_id: &str, val: T) {
|
pub fn set_cell<T: Into<CellType>>(&mut self, cell_id: &str, val: T) {
|
||||||
let loc = Self::parse_to_idx(cell_id);
|
let loc = Self::parse_to_idx(cell_id);
|
||||||
self.set_cell_raw(loc, val);
|
self.set_cell_raw(loc, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cell_raw<T: Into<Box<dyn Cell>>>(&mut self, (x,y): (usize, usize), val: T) {
|
pub fn set_cell_raw<T: Into<CellType>>(&mut self, (x,y): (usize, usize), val: T) {
|
||||||
// TODO check oob
|
// TODO check oob
|
||||||
self.cells[x][y] = Some(val.into());
|
self.cells[x][y] = Some(val.into());
|
||||||
}
|
}
|
||||||
@@ -104,13 +104,15 @@ impl Grid {
|
|||||||
/// A6,
|
/// A6,
|
||||||
/// F0,
|
/// F0,
|
||||||
/// etc
|
/// etc
|
||||||
pub fn get_cell(&self, cell_id: &str) -> &Option<Box<dyn Cell>> {
|
pub fn get_cell(&self, cell_id: &str) -> &Option<CellType> {
|
||||||
let (x, y) = Self::parse_to_idx(cell_id);
|
let (x, y) = Self::parse_to_idx(cell_id);
|
||||||
self.get_cell_raw(x, y)
|
self.get_cell_raw(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cell_raw(&self, x: usize, y: usize) -> &Option<Box<dyn Cell>> {
|
pub fn get_cell_raw(&self, x: usize, y: usize) -> &Option<CellType> {
|
||||||
// TODO check oob
|
if x >= LEN || y >= LEN {
|
||||||
|
return &None
|
||||||
|
}
|
||||||
&self.cells[x][y]
|
&self.cells[x][y]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,68 +146,50 @@ impl Default for Grid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Cell {
|
pub enum CellType {
|
||||||
/// Important! This is IS NOT the return value of an equation.
|
Number(f64),
|
||||||
/// This is the raw equation it's self.
|
String(String),
|
||||||
fn as_raw_string(&self) -> String;
|
Equation(String),
|
||||||
fn can_be_number(&self) -> bool;
|
}
|
||||||
fn as_num(&self) -> f64;
|
|
||||||
fn is_equation(&self) -> bool {
|
impl Into<CellType> for f64 {
|
||||||
self.as_raw_string().starts_with('=')
|
fn into(self) -> CellType {
|
||||||
|
CellType::duck_type(self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for dyn Cell {
|
impl Into<CellType> for String {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn into(self) -> CellType {
|
||||||
|
CellType::duck_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let disp = if self.can_be_number() {
|
impl CellType {
|
||||||
self.as_num().to_string()
|
fn duck_type<'a>(value: impl Into<String>) -> Self {
|
||||||
|
let value = value.into();
|
||||||
|
|
||||||
|
if let Ok(parse) = value.parse::<f64>() {
|
||||||
|
Self::Number(parse)
|
||||||
} else {
|
} else {
|
||||||
self.as_raw_string()
|
if value.starts_with('=') {
|
||||||
|
Self::Equation(value)
|
||||||
|
} else {
|
||||||
|
Self::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CellType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let d = match self {
|
||||||
|
CellType::Number(n) => n.to_string(),
|
||||||
|
CellType::String(n) => n.to_owned(),
|
||||||
|
CellType::Equation(r) => {
|
||||||
|
r.to_owned()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
write!(f, "{d}")
|
||||||
write!(f, "{disp}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cell for f64 {
|
|
||||||
fn as_raw_string(&self) -> String {
|
|
||||||
ToString::to_string(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_be_number(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_num(&self) -> f64 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Box<dyn Cell>> for f64 {
|
|
||||||
fn into(self) -> Box<dyn Cell> {
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Box<dyn Cell>> for String {
|
|
||||||
fn into(self) -> Box<dyn Cell> {
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cell for String {
|
|
||||||
fn as_raw_string(&self) -> String {
|
|
||||||
ToString::to_string(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_be_number(&self) -> bool {
|
|
||||||
// checking if the string is an equation
|
|
||||||
self.starts_with('=')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_num(&self) -> f64 {
|
|
||||||
unimplemented!("&str cannot be used in a numeric context")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +202,7 @@ fn test_cells() {
|
|||||||
assert!(grid.get_cell("A0").is_some());
|
assert!(grid.get_cell("A0").is_some());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.get_cell("A0").as_ref().unwrap().as_raw_string(),
|
grid.get_cell("A0").as_ref().unwrap().to_string(),
|
||||||
String::from("Hello")
|
String::from("Hello")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -246,3 +230,75 @@ fn i_to_c() {
|
|||||||
assert_eq!(Grid::num_to_char(51), "AZ");
|
assert_eq!(Grid::num_to_char(51), "AZ");
|
||||||
assert_eq!(Grid::num_to_char(701), "ZZ");
|
assert_eq!(Grid::num_to_char(701), "ZZ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math() {
|
||||||
|
use evalexpr::*;
|
||||||
|
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
grid.set_cell("A0", 2.);
|
||||||
|
grid.set_cell("B0", 1.);
|
||||||
|
grid.set_cell("C0", "=A0+B0".to_string());
|
||||||
|
|
||||||
|
assert_eq!(eval("1+2").unwrap(), Value::Int(3));
|
||||||
|
if let Some(cell) = grid.get_cell("C0") {
|
||||||
|
match cell {
|
||||||
|
CellType::Number(_) => todo!(),
|
||||||
|
CellType::String(_) => todo!(),
|
||||||
|
CellType::Equation(a) => {
|
||||||
|
let res = grid.evaluate(&a);
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 3.);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Should've found the value and returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fn_of_fn() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
grid.set_cell("A0", 2.);
|
||||||
|
grid.set_cell("B0", 1.);
|
||||||
|
grid.set_cell("C0", "=A0+B0".to_string());
|
||||||
|
grid.set_cell("D0", "=C0*2".to_string());
|
||||||
|
|
||||||
|
if let Some(cell) = grid.get_cell("D0") {
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 6.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn circular_reference_cells() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
grid.set_cell("A0", "=B0".to_string());
|
||||||
|
grid.set_cell("B0", "=A0".to_string());
|
||||||
|
|
||||||
|
if let Some(cell) = grid.get_cell("A0") {
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fn_of_fn_one_shot() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
grid.set_cell("A0", 2.);
|
||||||
|
grid.set_cell("B0", 1.);
|
||||||
|
grid.set_cell("C0", "=A0+B0".to_string());
|
||||||
|
grid.set_cell("D0", "=C0*2".to_string());
|
||||||
|
|
||||||
|
grid.set_cell("E0", "=D0+C0".to_string());
|
||||||
|
|
||||||
|
if let Some(cell) = grid.get_cell("E0") {
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 9.);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use evalexpr::{error::EvalexprResultValue, *};
|
use evalexpr::{error::EvalexprResultValue, *};
|
||||||
|
|
||||||
use crate::Grid;
|
use crate::app::calc::Grid;
|
||||||
|
|
||||||
pub struct CallbackContext<'a, T: EvalexprNumericTypes = DefaultNumericTypes> {
|
pub struct CallbackContext<'a, T: EvalexprNumericTypes = DefaultNumericTypes> {
|
||||||
variables: &'a Grid,
|
variables: &'a Grid,
|
||||||
@@ -40,8 +40,16 @@ impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
|||||||
|
|
||||||
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
||||||
if let Some(v) = self.variables.get_cell(identifier) {
|
if let Some(v) = self.variables.get_cell(identifier) {
|
||||||
if v.can_be_number() {
|
|
||||||
return Some(Value::Float(v.as_num()));
|
match v {
|
||||||
|
super::calc::CellType::Number(n) => return Some(Value::Float(n.to_owned())),
|
||||||
|
super::calc::CellType::String(s) => unimplemented!("{s}"),
|
||||||
|
super::calc::CellType::Equation(eq) => {
|
||||||
|
match eval_with_context(&eq[1..], self) {
|
||||||
|
Ok(e) => return Some(e),
|
||||||
|
Err(e) => panic!("{e} \"{eq}\""),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
39
src/app/error_msg.rs
Normal file
39
src/app/error_msg.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use ratatui::{layout::Rect, prelude, style::{Color, Style}, widgets::{Paragraph, Widget}};
|
||||||
|
|
||||||
|
pub struct ErrorMessage {
|
||||||
|
start: Instant,
|
||||||
|
error_msg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorMessage {
|
||||||
|
pub fn new(msg: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
error_msg: Some(msg.into()),
|
||||||
|
start: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
start: Instant::now(),
|
||||||
|
error_msg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &ErrorMessage {
|
||||||
|
fn render(self, area: Rect, buf: &mut prelude::Buffer) {
|
||||||
|
// The screen doesn't refresh at a fixed fps like a normal GUI,
|
||||||
|
// so if the user isn't moving around the timeout will *happen* but
|
||||||
|
// won't be visualized until they move
|
||||||
|
let msg = if self.start.elapsed().as_secs() > 3 {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
self.error_msg.clone().unwrap_or(String::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
Paragraph::new(msg).style(Style::new().fg(Color::Red)).render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod calc;
|
mod calc;
|
||||||
pub mod mode;
|
mod mode;
|
||||||
|
mod error_msg;
|
||||||
|
mod ctx;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::fmt::Display;
|
use std::{cmp::{max, min}, fmt::Display};
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect, prelude, style::{Color, Modifier, Style}, widgets::{Paragraph, Widget}
|
prelude, style::{Color, Style}, widgets::{Paragraph, Widget}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::{app::App};
|
use crate::app::{app::App, calc::LEN, error_msg::ErrorMessage};
|
||||||
|
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Insert(Editor),
|
Insert(Editor),
|
||||||
@@ -14,37 +14,33 @@ pub enum Mode {
|
|||||||
Visual((usize, usize)),
|
Visual((usize, usize)),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &Mode {
|
|
||||||
fn render(self, area: Rect, buf: &mut prelude::Buffer) {
|
|
||||||
let style = match self {
|
|
||||||
// Where you are typing - italic
|
|
||||||
Mode::Insert(_) => Style::new().fg(Color::Blue).add_modifier(Modifier::ITALIC),
|
|
||||||
Mode::Command(_) => Style::new().fg(Color::LightGreen).add_modifier(Modifier::ITALIC),
|
|
||||||
Mode::Chord(_) => Style::new().fg(Color::Blue).add_modifier(Modifier::ITALIC),
|
|
||||||
// Movement-based modes
|
|
||||||
Mode::Visual(_) => Style::new().fg(Color::Yellow),
|
|
||||||
Mode::Normal => Style::new().fg(Color::Green),
|
|
||||||
};
|
|
||||||
|
|
||||||
Paragraph::new(self.to_string())
|
|
||||||
.style(style)
|
|
||||||
.render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Mode {
|
impl Display for Mode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Mode::Normal => write!(f, "-- NORMAL --"),
|
Mode::Normal => write!(f, "NORMAL"),
|
||||||
Mode::Insert(_) => write!(f, "-- INSERT --"),
|
Mode::Insert(_) => write!(f, "INSERT"),
|
||||||
Mode::Chord(_) => write!(f, "-- CHORD --"),
|
Mode::Chord(_) => write!(f, "CHORD"),
|
||||||
Mode::Command(_) => write!(f, "-- COMMAND --"),
|
Mode::Command(_) => write!(f, "COMMAND"),
|
||||||
Mode::Visual(_) => write!(f, "-- VISUAL --"),
|
Mode::Visual(_) => write!(f, "VISUAL"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
|
|
||||||
|
pub fn get_style(&self) -> Style {
|
||||||
|
match self {
|
||||||
|
// Where you are typing
|
||||||
|
Mode::Insert(_) => Style::new().fg(Color::White).bg(Color::Blue),
|
||||||
|
Mode::Command(_) => Style::new().fg(Color::Black).bg(Color::Magenta),
|
||||||
|
Mode::Chord(_) => Style::new().fg(Color::Black).bg(Color::LightBlue),
|
||||||
|
// Movement-based modes
|
||||||
|
Mode::Visual(_) => Style::new().fg(Color::Yellow),
|
||||||
|
Mode::Normal => Style::new().fg(Color::Green),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn process_cmd(app: &mut App) {
|
pub fn process_cmd(app: &mut App) {
|
||||||
if let Mode::Command(editor) = &mut app.mode {
|
if let Mode::Command(editor) = &mut app.mode {
|
||||||
// [':', 'q']
|
// [':', 'q']
|
||||||
@@ -53,7 +49,7 @@ impl Mode {
|
|||||||
if let Some(file) = &app.file {
|
if let Some(file) = &app.file {
|
||||||
unimplemented!("Figure out how we want to save Grid to a csv or something")
|
unimplemented!("Figure out how we want to save Grid to a csv or something")
|
||||||
} else {
|
} else {
|
||||||
// TODO figure out how to get an error message to the user
|
app.error_msg = ErrorMessage::new("No file selected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'q' => app.exit = true,
|
'q' => app.exit = true,
|
||||||
@@ -73,7 +69,7 @@ impl Mode {
|
|||||||
}
|
}
|
||||||
// v
|
// v
|
||||||
'j' => {
|
'j' => {
|
||||||
app.grid.selected_cell.1 = app.grid.selected_cell.1.saturating_add(1);
|
app.grid.selected_cell.1 = min(app.grid.selected_cell.1.saturating_add(1), LEN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ^
|
// ^
|
||||||
@@ -83,7 +79,7 @@ impl Mode {
|
|||||||
}
|
}
|
||||||
// >
|
// >
|
||||||
'l' => {
|
'l' => {
|
||||||
app.grid.selected_cell.0 = app.grid.selected_cell.0.saturating_add(1);
|
app.grid.selected_cell.0 = min(app.grid.selected_cell.0.saturating_add(1), LEN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
'0' => {
|
'0' => {
|
||||||
@@ -95,7 +91,7 @@ impl Mode {
|
|||||||
let (x, y) = app.grid.selected_cell;
|
let (x, y) = app.grid.selected_cell;
|
||||||
|
|
||||||
let val =
|
let val =
|
||||||
app.grid.get_cell_raw(x, y).as_ref().map(|f| f.as_raw_string()).unwrap_or(String::new());
|
app.grid.get_cell_raw(x, y).as_ref().map(|f| f.to_string()).unwrap_or(String::new());
|
||||||
|
|
||||||
app.mode = Mode::Insert(Editor::new(val, (x, y)));
|
app.mode = Mode::Insert(Editor::new(val, (x, y)));
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/main.rs
29
src/main.rs
@@ -1,30 +1,6 @@
|
|||||||
mod app;
|
mod app;
|
||||||
mod ctx;
|
|
||||||
|
|
||||||
use crate::app::{app::App, calc::{Grid}};
|
use crate::app::{app::App};
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_math() {
|
|
||||||
use evalexpr::*;
|
|
||||||
|
|
||||||
let mut grid = Grid::new();
|
|
||||||
grid.set_cell("A0", 2.);
|
|
||||||
grid.set_cell("B0", 1.);
|
|
||||||
grid.set_cell("C0", "=A0+B0".to_string());
|
|
||||||
|
|
||||||
assert_eq!(eval("1+2").unwrap(), Value::Int(3));
|
|
||||||
|
|
||||||
let cell_text = &grid.get_cell("C0");
|
|
||||||
if let Some(text) = cell_text {
|
|
||||||
if text.is_equation() {
|
|
||||||
println!("{}", text.as_raw_string());
|
|
||||||
let display = grid.evaluate(&text.as_raw_string());
|
|
||||||
assert_eq!(display, Some(3.));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic!("Should've found the value and returned");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
let term = ratatui::init();
|
let term = ratatui::init();
|
||||||
@@ -36,6 +12,9 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
app.grid.set_cell("C0", "Fruit".to_string());
|
app.grid.set_cell("C0", "Fruit".to_string());
|
||||||
app.grid.set_cell("C1", "=A1+B1".to_string());
|
app.grid.set_cell("C1", "=A1+B1".to_string());
|
||||||
|
|
||||||
|
app.grid.set_cell("D0", "x2".to_string());
|
||||||
|
app.grid.set_cell("D1", "=C1*2".to_string());
|
||||||
|
|
||||||
let res = app.run(term);
|
let res = app.run(term);
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
Reference in New Issue
Block a user