Compare commits

...

8 Commits

Author SHA1 Message Date
Joe Bellus 0667a9677b Editor Fixes (#5)
Reviewed-on: #5
2022-10-20 01:52:58 +00:00
Joe Bellus 5e9f0db4bd Linting fixes
continuous-integration/drone/pr Build is passing Details
2022-10-19 21:41:47 -04:00
Joe Bellus 564a243be2 Merge branch 'main' into editor-fixes
continuous-integration/drone/pr Build is failing Details
2022-10-20 00:41:46 +00:00
Joe Bellus 6d67b476c4 Initial Editor Focus
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
Initial editor is now focused after launch
2022-10-19 20:35:36 -04:00
Joe Bellus 3e4b69ee49 Added Simple Series Aggregation Functions
Added Series.max, Series.min, Series.head, Series.tail, Series.mean

Added Range collection

(start..end).collect() now produces an array
2022-10-19 20:24:17 -04:00
Joe Bellus 5943876569 Added yank editor commands
continuous-integration/drone/push Build is passing Details
Yank, yank line, and paste were added to normal mode keybinds
2022-10-17 15:12:47 -04:00
Joe Bellus 3b6f7cdbf4 Implemented Change Word
continuous-integration/drone/push Build is passing Details
Change word forward and backward were implemented in the editor_data
and bound in the editor to "cw" and "cb" respectively.

Scan word forward and scan word backward now utilize a shared function
that gets the scan position. This is now also used for delete word
forward/backward, so they can share logic

Updated keybinds in README
2022-10-17 02:00:25 -04:00
Joe Bellus e239573903 Shift Arrow Selection
Fixed issues where shift + arrow keys would not select text. This
creates an issue with cursor movements after using shift + arrow
keys. Further cursor movements retain the selection mode and allow
selecting. Cursor movements should clear the active
selection. However, this would invalidate the ability for selecting
via marking selection (v).

Selection marking will be refactored, which will fix this inconsistency.
2022-10-17 01:27:22 -04:00
6 changed files with 210 additions and 68 deletions

View File

@ -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");
}
}

View File

@ -1,5 +1,3 @@
use std::sync::Arc;
use crate::dataframe;
#[derive(Debug)]

View File

@ -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.

View File

@ -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,

View File

@ -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)
}

View File

@ -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();
}
_ => {}
}
}