fix #2
This commit is contained in:
@@ -11,6 +11,7 @@ use ratatui::{
|
||||
|
||||
use crate::app::{
|
||||
calc::{Grid, LEN},
|
||||
error_msg::ErrorMessage,
|
||||
mode::Mode,
|
||||
};
|
||||
|
||||
@@ -19,6 +20,7 @@ pub struct App {
|
||||
pub grid: Grid,
|
||||
pub mode: Mode,
|
||||
pub file: Option<PathBuf>,
|
||||
pub error_msg: ErrorMessage,
|
||||
}
|
||||
|
||||
impl Widget for &App {
|
||||
@@ -75,14 +77,13 @@ impl Widget for &App {
|
||||
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
||||
|
||||
match (x == 0, y == 0) {
|
||||
// 0,0 dead space
|
||||
(true, true) => {
|
||||
let (x, y) = self.grid.selected_cell;
|
||||
let c = Grid::num_to_char(x);
|
||||
display = format!("{y}{c}",);
|
||||
style = Style::new().fg(Color::Green).bg(Color::Black);
|
||||
display = self.mode.to_string();
|
||||
style = self.mode.get_style();
|
||||
}
|
||||
// row names
|
||||
(true, false) => {
|
||||
// row names
|
||||
display = y_idx.to_string();
|
||||
|
||||
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);
|
||||
}
|
||||
// column names
|
||||
(false, true) => {
|
||||
// column names
|
||||
display = Grid::num_to_char(x_idx);
|
||||
|
||||
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)
|
||||
}
|
||||
// grid squares
|
||||
(false, false) => {
|
||||
if let Some(cell) = self.grid.get_cell_raw(x_idx, y_idx) {
|
||||
display = cell.as_raw_string();
|
||||
|
||||
if cell.can_be_number() {
|
||||
if let Some(val) = self.grid.evaluate(&cell.as_raw_string()) {
|
||||
display = val.to_string();
|
||||
style = Style::new()
|
||||
.underline_color(Color::DarkGray)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
} else {
|
||||
// broken formulas
|
||||
if cell.is_equation() {
|
||||
match cell {
|
||||
crate::app::calc::CellType::Number(c) => display = c.to_string(),
|
||||
crate::app::calc::CellType::String(s) => display = s.to_owned(),
|
||||
crate::app::calc::CellType::Equation(e) => {
|
||||
if let Some(val) = self.grid.evaluate(e) {
|
||||
display = val.to_string();
|
||||
style = Style::new()
|
||||
.underline_color(Color::DarkGray)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
} else {
|
||||
style =
|
||||
Style::new().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED)
|
||||
}
|
||||
@@ -152,6 +153,7 @@ impl App {
|
||||
grid: Grid::new(),
|
||||
mode: Mode::Normal,
|
||||
file: None,
|
||||
error_msg: ErrorMessage::none(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +193,7 @@ impl App {
|
||||
Mode::Normal => frame.render_widget(
|
||||
Paragraph::new({
|
||||
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
|
||||
}),
|
||||
cmd_line_left,
|
||||
@@ -200,7 +202,7 @@ impl App {
|
||||
}
|
||||
|
||||
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<()> {
|
||||
@@ -251,9 +253,9 @@ impl App {
|
||||
event::KeyCode::Char(c) => {
|
||||
Mode::process_key(self, c);
|
||||
}
|
||||
_ => todo!(),
|
||||
_ => {}
|
||||
},
|
||||
_ => todo!(),
|
||||
_ => {}
|
||||
},
|
||||
Mode::Visual(_start_pos) => {
|
||||
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 crate::ctx;
|
||||
use crate::app::ctx;
|
||||
|
||||
// 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 {
|
||||
// a b c ...
|
||||
@@ -13,7 +13,7 @@ pub struct Grid {
|
||||
// 1
|
||||
// 2
|
||||
// ...
|
||||
cells: [[Option<Box<dyn Cell>>; LEN]; LEN],
|
||||
cells: [[Option<CellType>; LEN]; LEN],
|
||||
/// (X, Y)
|
||||
pub selected_cell: (usize, usize),
|
||||
}
|
||||
@@ -29,7 +29,7 @@ impl std::fmt::Debug for Grid {
|
||||
impl Grid {
|
||||
pub fn new() -> Self {
|
||||
// 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));
|
||||
|
||||
Self {
|
||||
@@ -90,12 +90,12 @@ impl Grid {
|
||||
(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);
|
||||
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
|
||||
self.cells[x][y] = Some(val.into());
|
||||
}
|
||||
@@ -104,13 +104,15 @@ impl Grid {
|
||||
/// A6,
|
||||
/// F0,
|
||||
/// 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);
|
||||
self.get_cell_raw(x, y)
|
||||
}
|
||||
|
||||
pub fn get_cell_raw(&self, x: usize, y: usize) -> &Option<Box<dyn Cell>> {
|
||||
// TODO check oob
|
||||
pub fn get_cell_raw(&self, x: usize, y: usize) -> &Option<CellType> {
|
||||
if x >= LEN || y >= LEN {
|
||||
return &None
|
||||
}
|
||||
&self.cells[x][y]
|
||||
}
|
||||
|
||||
@@ -144,68 +146,50 @@ impl Default for Grid {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Cell {
|
||||
/// Important! This is IS NOT the return value of an equation.
|
||||
/// This is the raw equation it's self.
|
||||
fn as_raw_string(&self) -> String;
|
||||
fn can_be_number(&self) -> bool;
|
||||
fn as_num(&self) -> f64;
|
||||
fn is_equation(&self) -> bool {
|
||||
self.as_raw_string().starts_with('=')
|
||||
pub enum CellType {
|
||||
Number(f64),
|
||||
String(String),
|
||||
Equation(String),
|
||||
}
|
||||
|
||||
impl Into<CellType> for f64 {
|
||||
fn into(self) -> CellType {
|
||||
CellType::duck_type(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for dyn Cell {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl Into<CellType> for String {
|
||||
fn into(self) -> CellType {
|
||||
CellType::duck_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
let disp = if self.can_be_number() {
|
||||
self.as_num().to_string()
|
||||
impl CellType {
|
||||
fn duck_type<'a>(value: impl Into<String>) -> Self {
|
||||
let value = value.into();
|
||||
|
||||
if let Ok(parse) = value.parse::<f64>() {
|
||||
Self::Number(parse)
|
||||
} 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, "{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")
|
||||
write!(f, "{d}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +202,7 @@ fn test_cells() {
|
||||
assert!(grid.get_cell("A0").is_some());
|
||||
|
||||
assert_eq!(
|
||||
grid.get_cell("A0").as_ref().unwrap().as_raw_string(),
|
||||
grid.get_cell("A0").as_ref().unwrap().to_string(),
|
||||
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(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 crate::Grid;
|
||||
use crate::app::calc::Grid;
|
||||
|
||||
pub struct CallbackContext<'a, T: EvalexprNumericTypes = DefaultNumericTypes> {
|
||||
variables: &'a Grid,
|
||||
@@ -40,8 +40,16 @@ impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
||||
|
||||
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
||||
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;
|
||||
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 calc;
|
||||
pub mod mode;
|
||||
mod calc;
|
||||
mod mode;
|
||||
mod error_msg;
|
||||
mod ctx;
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::fmt::Display;
|
||||
use std::{cmp::{max, min}, fmt::Display};
|
||||
|
||||
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 {
|
||||
Insert(Editor),
|
||||
@@ -14,37 +14,33 @@ pub enum Mode {
|
||||
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 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Mode::Normal => write!(f, "-- NORMAL --"),
|
||||
Mode::Insert(_) => write!(f, "-- INSERT --"),
|
||||
Mode::Chord(_) => write!(f, "-- CHORD --"),
|
||||
Mode::Command(_) => write!(f, "-- COMMAND --"),
|
||||
Mode::Visual(_) => write!(f, "-- VISUAL --"),
|
||||
Mode::Normal => write!(f, "NORMAL"),
|
||||
Mode::Insert(_) => write!(f, "INSERT"),
|
||||
Mode::Chord(_) => write!(f, "CHORD"),
|
||||
Mode::Command(_) => write!(f, "COMMAND"),
|
||||
Mode::Visual(_) => write!(f, "VISUAL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if let Mode::Command(editor) = &mut app.mode {
|
||||
// [':', 'q']
|
||||
@@ -53,7 +49,7 @@ impl Mode {
|
||||
if let Some(file) = &app.file {
|
||||
unimplemented!("Figure out how we want to save Grid to a csv or something")
|
||||
} 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,
|
||||
@@ -73,7 +69,7 @@ impl Mode {
|
||||
}
|
||||
// v
|
||||
'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;
|
||||
}
|
||||
// ^
|
||||
@@ -83,7 +79,7 @@ impl Mode {
|
||||
}
|
||||
// >
|
||||
'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;
|
||||
}
|
||||
'0' => {
|
||||
@@ -95,7 +91,7 @@ impl Mode {
|
||||
let (x, y) = app.grid.selected_cell;
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
29
src/main.rs
29
src/main.rs
@@ -1,30 +1,6 @@
|
||||
mod app;
|
||||
mod ctx;
|
||||
|
||||
use crate::app::{app::App, calc::{Grid}};
|
||||
|
||||
#[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");
|
||||
}
|
||||
use crate::app::{app::App};
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
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("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);
|
||||
ratatui::restore();
|
||||
return res;
|
||||
|
||||
Reference in New Issue
Block a user