commit
0667a9677b
|
@ -25,8 +25,14 @@ pub fn setup_engine(engine: &mut rhai::Engine) {
|
|||
engine.register_fn("to_series", script_functions::to_series_unnamed);
|
||||
engine.register_fn("series", script_functions::series_unnamed);
|
||||
engine.register_fn("head", Series::s_head);
|
||||
engine.register_fn("head", Series::s_head_len);
|
||||
engine.register_fn("tail", Series::s_tail);
|
||||
engine.register_fn("tail", Series::s_tail_len);
|
||||
engine.register_fn("sort", Series::s_sort);
|
||||
engine.register_fn("sum", Series::s_sum);
|
||||
engine.register_fn("mean", Series::s_mean);
|
||||
engine.register_fn("min", Series::s_min);
|
||||
engine.register_fn("max", Series::s_max);
|
||||
engine.register_fn("add", Series::s_op_add);
|
||||
|
||||
engine.register_fn("+", Series::add);
|
||||
|
@ -67,6 +73,7 @@ pub fn setup_engine(engine: &mut rhai::Engine) {
|
|||
engine.register_fn("min", script_functions::min);
|
||||
engine.register_fn("max", script_functions::max);
|
||||
engine.register_fn("first", script_functions::first);
|
||||
engine.register_fn("collect", script_functions::range_to_array);
|
||||
let _ = engine.register_custom_operator("gt", 200);
|
||||
let _ = engine.register_custom_operator("gte", 200);
|
||||
let _ = engine.register_custom_operator("<<", 200);
|
||||
|
@ -217,9 +224,22 @@ impl Add for Series {
|
|||
}
|
||||
|
||||
impl Series {
|
||||
pub fn s_head(&mut self, n: i64) -> Series {
|
||||
pub fn s_head(&mut self) -> Series {
|
||||
Series(self.0.head(Some(1)))
|
||||
}
|
||||
|
||||
pub fn s_head_len(&mut self, n: i64) -> Series {
|
||||
Series(self.0.head(Some(n as usize)))
|
||||
}
|
||||
|
||||
pub fn s_tail(&mut self) -> Series {
|
||||
Series(self.0.tail(Some(self.0.len() - 1)))
|
||||
}
|
||||
|
||||
pub fn s_tail_len(&mut self, size: i64) -> Series {
|
||||
Series(self.0.tail(Some(size as usize)))
|
||||
}
|
||||
|
||||
pub fn s_sort(&mut self, reverse: bool) -> Series {
|
||||
Series(self.0.sort(reverse))
|
||||
}
|
||||
|
@ -228,6 +248,18 @@ impl Series {
|
|||
self.0.sum().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn s_mean(&mut self) -> f64 {
|
||||
self.0.mean().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn s_min(&mut self) -> f64 {
|
||||
self.0.min().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn s_max(&mut self) -> f64 {
|
||||
self.0.max().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn s_get(&mut self, n: i64) -> ScriptResult<rhai::Dynamic> {
|
||||
let value = self.get(n as usize);
|
||||
match value {
|
||||
|
@ -436,6 +468,10 @@ mod script_functions {
|
|||
pub fn gte_op(a: &str, b: rhai::Dynamic) -> DataFrameExpression {
|
||||
DataFrameExpression(polars::prelude::col(a).gt_eq(DataFrameExpression::from(b)))
|
||||
}
|
||||
|
||||
pub fn range_to_array(range: std::ops::Range<i64>) -> rhai::Array {
|
||||
range.map(Dynamic::from).collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn implementation_df_select(
|
||||
|
@ -690,4 +726,40 @@ from data ["age"] : ["age" gt 18];
|
|||
.collect::<Vec<_>>();
|
||||
assert_eq!(s, vec![Some(22), Some(32)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_range_to_array() {
|
||||
let res = process(r#"series((1..4).collect())"#);
|
||||
let s = res
|
||||
.into_series()
|
||||
.i64()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(s, vec![Some(1), Some(2), Some(3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_series_min() {
|
||||
let res = process(r#"series((2..4).collect()).min()"#);
|
||||
dbg!(&res);
|
||||
let s = res.into_scalar();
|
||||
assert_eq!(s, "2.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_series_max() {
|
||||
let res = process(r#"series((2..4).collect()).max()"#);
|
||||
dbg!(&res);
|
||||
let s = res.into_scalar();
|
||||
assert_eq!(s, "3.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_series_mean() {
|
||||
let res = process(r#"series((1..51).collect()).mean()"#);
|
||||
dbg!(&res);
|
||||
let s = res.into_scalar();
|
||||
assert_eq!(s, "25.5");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::dataframe;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -32,6 +32,13 @@ In normal mode the cursor is a block and functional keybinds can be used for mov
|
|||
| w | Scan forward a word |
|
||||
| v | Mark selection |
|
||||
| x | Delete current character or selection |
|
||||
| dd | Delete the current line |
|
||||
| cw | Change word forward |
|
||||
| cb | Change word backward |
|
||||
| y | Yank selection |
|
||||
| Y | Yank line |
|
||||
| p | Paste line at cursor |
|
||||
| P | Paste line below |
|
||||
|
||||
** Insert mode
|
||||
In insert mode the cursor is a line and text can be edited. To return to normal mode use the ESC key.
|
||||
|
|
|
@ -46,26 +46,6 @@ impl Default for AppData {
|
|||
}
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
pub fn process(&mut self) {
|
||||
let mut engine = abacus_core::Engine::default();
|
||||
|
||||
for mut block in self.blocks.iter_mut() {
|
||||
let start = std::time::Instant::now();
|
||||
block.output = engine.process_script(&block.editor_data.content.to_string());
|
||||
println!("block executed in {}", start.elapsed().as_millis());
|
||||
}
|
||||
}
|
||||
pub fn process_block(&mut self, index: usize) {
|
||||
let mut engine = abacus_core::Engine::default();
|
||||
if let Some(block) = self.blocks.get_mut(index) {
|
||||
let start = std::time::Instant::now();
|
||||
block.output = engine.process_script(&block.editor_data.content.to_string());
|
||||
println!("block executed in {}", start.elapsed().as_millis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Data, Lens, Default, PartialEq, Debug)]
|
||||
pub struct Block {
|
||||
pub name: String,
|
||||
|
|
|
@ -104,37 +104,63 @@ impl EditorData {
|
|||
self.deselect();
|
||||
}
|
||||
|
||||
pub fn word_scan_forward(&mut self) {
|
||||
while self.cursor_pos < self.content.len_chars() {
|
||||
self.cursor_pos += 1;
|
||||
if !self
|
||||
.current_char()
|
||||
.map(|c| c.is_alphanumeric())
|
||||
.unwrap_or(false)
|
||||
pub fn get_word_scan_forward_position(&mut self, skip_first: bool) -> usize {
|
||||
if self.cursor_pos < self.content.len_chars() {
|
||||
for (idx, ch) in self
|
||||
.content
|
||||
.chars_at(self.cursor_pos)
|
||||
.skip(if skip_first { 1 } else { 0 })
|
||||
.enumerate()
|
||||
{
|
||||
break;
|
||||
if !ch.is_alphanumeric() {
|
||||
return (idx + self.cursor_pos + 2).min(self.content.len_chars());
|
||||
}
|
||||
}
|
||||
self.deselect();
|
||||
self.content.len_chars()
|
||||
} else {
|
||||
self.content.len_chars()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn word_scan_backward(&mut self) {
|
||||
if self.cursor_pos > 0 {
|
||||
self.cursor_pos -= 1;
|
||||
pub fn get_word_scan_backward_position(&mut self, skip_first: bool) -> usize {
|
||||
let mut pos = self.cursor_pos;
|
||||
if pos > 0 && skip_first {
|
||||
pos -= 1;
|
||||
}
|
||||
while self.cursor_pos > 0 {
|
||||
while pos > 0 {
|
||||
if !self
|
||||
.content
|
||||
.get_char(self.cursor_pos - 1)
|
||||
.get_char(pos - 1)
|
||||
.map(|c| c.is_alphanumeric())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
self.cursor_pos -= 1;
|
||||
pos -= 1;
|
||||
}
|
||||
self.deselect();
|
||||
}
|
||||
pos
|
||||
}
|
||||
|
||||
pub fn delete_word_forward(&mut self) {
|
||||
let end = self.get_word_scan_forward_position(false);
|
||||
self.content.remove(self.cursor_pos..end);
|
||||
}
|
||||
|
||||
pub fn delete_word_backward(&mut self) {
|
||||
let start = self.get_word_scan_backward_position(true);
|
||||
self.content.remove(start..self.cursor_pos);
|
||||
self.cursor_pos = start;
|
||||
}
|
||||
|
||||
pub fn word_scan_forward(&mut self) {
|
||||
self.cursor_pos = self.get_word_scan_forward_position(true);
|
||||
self.deselect();
|
||||
}
|
||||
|
||||
pub fn word_scan_backward(&mut self) {
|
||||
self.cursor_pos = self.get_word_scan_backward_position(true);
|
||||
self.deselect();
|
||||
}
|
||||
|
||||
pub fn delete_char_forward(&mut self) {
|
||||
|
@ -201,15 +227,8 @@ impl EditorData {
|
|||
}
|
||||
|
||||
pub fn select_up(&mut self) {
|
||||
let line_idx = self.content.char_to_line(self.cursor_pos);
|
||||
|
||||
if line_idx > 0 {
|
||||
let start_of_current_line = self.content.line_to_char(line_idx);
|
||||
let line_pos = self.cursor_pos - start_of_current_line;
|
||||
let up_line_start = self.content.line_to_char(line_idx - 1);
|
||||
let up_line = self.content.line(line_idx - 1);
|
||||
self.cursor_pos = up_line_start + line_pos.min(up_line.len_chars() - 1);
|
||||
}
|
||||
self.start_selection();
|
||||
self.cursor_up();
|
||||
}
|
||||
|
||||
pub fn cursor_down(&mut self) {
|
||||
|
@ -225,17 +244,8 @@ impl EditorData {
|
|||
}
|
||||
|
||||
pub fn select_down(&mut self) {
|
||||
//12 -> 11
|
||||
let line_idx = self.content.char_to_line(self.cursor_pos);
|
||||
if line_idx < self.content.len_lines() - 1 {
|
||||
let start_of_current_line = self.content.line_to_char(line_idx);
|
||||
let line_pos = self.cursor_pos - start_of_current_line;
|
||||
let start_of_next_line = self.content.line_to_char(line_idx + 1) + 1;
|
||||
let next_line_len = self.content.line(line_idx + 1).len_chars();
|
||||
|
||||
self.cursor_pos =
|
||||
(start_of_next_line + line_pos.min(next_line_len)).min(start_of_next_line);
|
||||
}
|
||||
self.start_selection();
|
||||
self.cursor_down();
|
||||
}
|
||||
|
||||
pub fn cursor_right(&mut self) {
|
||||
|
@ -281,21 +291,24 @@ impl EditorData {
|
|||
self.deselect();
|
||||
}
|
||||
|
||||
pub fn insert_mode(&mut self) {
|
||||
self.mode = EditMode::Insert;
|
||||
self.deselect();
|
||||
}
|
||||
|
||||
pub fn select_all(&mut self) {
|
||||
self.selection_pos = Some(0);
|
||||
self.cursor_pos = self.content.len_chars();
|
||||
}
|
||||
|
||||
pub fn select_left(&mut self) {
|
||||
if self.cursor_pos > 0 {
|
||||
self.cursor_pos -= 1
|
||||
}
|
||||
self.start_selection();
|
||||
self.cursor_left();
|
||||
}
|
||||
|
||||
pub fn select_right(&mut self) {
|
||||
if self.cursor_pos < self.content.len_chars() {
|
||||
self.cursor_pos += 1;
|
||||
}
|
||||
self.start_selection();
|
||||
self.cursor_right();
|
||||
}
|
||||
|
||||
pub fn current_line(&self) -> ropey::RopeSlice {
|
||||
|
@ -310,6 +323,12 @@ impl EditorData {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn start_selection(&mut self) {
|
||||
if self.selection_pos.is_none() {
|
||||
self.selection_pos = Some(self.cursor_pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_line_index(&self) -> usize {
|
||||
self.content.char_to_line(self.cursor_pos)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@ mod keymap {
|
|||
pub const WORD_FORWARD: &str = "w";
|
||||
pub const WORD_BACK: &str = "b";
|
||||
pub const SELECT_MODE: &str = "v";
|
||||
pub const CHANGE_WORD_FORWARD: &str = "cw";
|
||||
pub const CHANGE_WORD_BACKWARD: &str = "cb";
|
||||
pub const YANK: &str = "y";
|
||||
pub const YANK_LINE: &str = "Y";
|
||||
pub const PASTE: &str = "p";
|
||||
pub const PASTE_LINE: &str = "P";
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct KeyList {
|
||||
|
@ -63,6 +69,12 @@ mod keymap {
|
|||
WORD_FORWARD,
|
||||
WORD_BACK,
|
||||
SELECT_MODE,
|
||||
CHANGE_WORD_FORWARD,
|
||||
CHANGE_WORD_BACKWARD,
|
||||
YANK,
|
||||
YANK_LINE,
|
||||
PASTE,
|
||||
PASTE_LINE,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +161,7 @@ impl AbacusEditor {
|
|||
}
|
||||
|
||||
if data.mode == EditMode::Normal {
|
||||
let char_rect = if data.content.len_chars() == 0 {
|
||||
let char_rect = if data.cursor_pos == 0 {
|
||||
let rects = layout.rects_for_range(0..1);
|
||||
rects.first().cloned()
|
||||
} else if data.current_char() == Some('\n')
|
||||
|
@ -358,7 +370,59 @@ impl Widget<EditorData> for AbacusEditor {
|
|||
data.word_scan_backward();
|
||||
}
|
||||
Some(keymap::SELECT_MODE) => {
|
||||
data.selection_pos = Some(data.cursor_pos);
|
||||
data.cursor_to_end_of_line();
|
||||
data.start_selection();
|
||||
data.cursor_to_start_of_line();
|
||||
}
|
||||
Some(keymap::CHANGE_WORD_FORWARD) => {
|
||||
data.delete_word_forward();
|
||||
data.insert_mode();
|
||||
}
|
||||
Some(keymap::CHANGE_WORD_BACKWARD) => {
|
||||
data.delete_word_backward();
|
||||
data.insert_mode();
|
||||
}
|
||||
Some(keymap::YANK) => {
|
||||
if data.selection_pos.is_some() {
|
||||
let mut cb: clipboard::ClipboardContext =
|
||||
clipboard::ClipboardProvider::new().unwrap();
|
||||
if let Some(slice) = data.content.get_slice(data.select_range())
|
||||
{
|
||||
let _ = cb.set_contents(slice.to_string());
|
||||
}
|
||||
}
|
||||
data.deselect();
|
||||
}
|
||||
Some(keymap::YANK_LINE) => {
|
||||
data.cursor_to_start_of_line();
|
||||
data.start_selection();
|
||||
data.cursor_to_end_of_line();
|
||||
if data.selection_pos.is_some() {
|
||||
let mut cb: clipboard::ClipboardContext =
|
||||
clipboard::ClipboardProvider::new().unwrap();
|
||||
if let Some(slice) = data.content.get_slice(data.select_range())
|
||||
{
|
||||
let _ = cb.set_contents(slice.to_string());
|
||||
}
|
||||
}
|
||||
data.deselect();
|
||||
}
|
||||
Some(keymap::PASTE) => {
|
||||
let mut cb: clipboard::ClipboardContext =
|
||||
clipboard::ClipboardProvider::new().unwrap();
|
||||
data.push_str(&cb.get_contents().unwrap_or_default());
|
||||
ctx.request_layout();
|
||||
}
|
||||
Some(keymap::PASTE_LINE) => {
|
||||
if data.selection_pos.is_some() {
|
||||
data.delete_char_back();
|
||||
}
|
||||
let mut cb: clipboard::ClipboardContext =
|
||||
clipboard::ClipboardProvider::new().unwrap();
|
||||
data.cursor_to_end_of_line();
|
||||
data.push_str("\n");
|
||||
data.push_str(&cb.get_contents().unwrap_or_default());
|
||||
ctx.request_layout();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -414,7 +478,6 @@ impl Widget<EditorData> for AbacusEditor {
|
|||
|
||||
druid::keyboard_types::Key::ArrowUp if e.mods.shift() => {
|
||||
data.select_up();
|
||||
data.deselect();
|
||||
}
|
||||
druid::keyboard_types::Key::ArrowDown if e.mods.shift() => {
|
||||
data.select_down();
|
||||
|
@ -482,6 +545,9 @@ impl Widget<EditorData> for AbacusEditor {
|
|||
ctx.request_paint();
|
||||
}
|
||||
}
|
||||
Event::WindowConnected => {
|
||||
ctx.request_focus();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue