Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ X Fixed the scroll-off-the-bottom issue
X Initial commands for the engine
* Output commands from engine to the line editor
* UTF-8 support
* History
* Up/down history
* Home/end
X Line buffer
X Backspace/delete
* Navigation
X History
X Up/down history
X Home/end
* Refactor keypresses to flush at the end
* Ctrl-A, Ctrl-K, etc
* Under status
* Async/parallel prompt
* Validation
Expand Down
36 changes: 28 additions & 8 deletions src/line_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@ impl LineBuffer {
}

pub fn inc_insertion_point(&mut self) {
// let char_indices: Vec<_> = self.buffer.char_indices().collect();

// for i in 0..char_indices.len() {

// }
// for (idx, c) in char_indices {
// if idx == self.insertion_point
// }
let grapheme_indices = self.get_grapheme_indices();
for i in 0..grapheme_indices.len() {
if grapheme_indices[i].0 == self.insertion_point && i < (grapheme_indices.len() - 1) {
Expand Down Expand Up @@ -109,6 +101,34 @@ impl LineBuffer {
self.buffer.clear()
}

// pub fn get_grapheme_index_left(&self) -> usize {
// let grapheme_indices = self.get_grapheme_indices();

// let mut prev = 0;
// for (idx, _) in grapheme_indices {
// if idx >= self.insertion_point {
// return prev;
// }
// prev = idx;
// }

// prev
// }

// pub fn get_grapheme_index_right(&self) -> usize {
// let grapheme_indices = self.get_grapheme_indices();

// let mut next = self.buffer.len();
// for (idx, _) in grapheme_indices.iter().rev() {
// if *idx <= self.insertion_point {
// return next;
// }
// next = *idx;
// }

// next
// }

pub fn move_word_left(&mut self) -> usize {
let mut words = self.buffer[..self.insertion_point]
.split_word_bound_indices()
Expand Down
172 changes: 93 additions & 79 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use std::io::{stdout, Write};

use crossterm::{
cursor::{position, MoveLeft, MoveRight, MoveToColumn, MoveToNextLine},
cursor::{
position, MoveLeft, MoveRight, MoveToColumn, MoveToNextLine, RestorePosition, SavePosition,
},
event::read,
event::{Event, KeyCode, KeyEvent, KeyModifiers},
style::{Color, Print, ResetColor, SetForegroundColor},
terminal, ExecutableCommand, QueueableCommand, Result,
terminal::{self, Clear, ClearType},
ExecutableCommand, QueueableCommand, Result,
};

use std::collections::VecDeque;
Expand All @@ -28,6 +31,24 @@ fn print_message(stdout: &mut Stdout, msg: &str) -> Result<()> {
Ok(())
}

fn buffer_repaint(
stdout: &mut Stdout,
buffer: &LineBuffer,
prompt_offset: u16,
new_index: usize,
) -> Result<()> {
let raw_buffer = buffer.get_buffer();

stdout.queue(MoveToColumn(prompt_offset))?;
stdout.queue(Print(&raw_buffer[0..new_index]))?;
stdout.queue(SavePosition)?;
stdout.queue(Print(&raw_buffer[new_index..]))?;
stdout.queue(Clear(ClearType::UntilNewLine))?;
stdout.queue(RestorePosition)?;

Ok(())
}

fn main() -> Result<()> {
let mut stdout = stdout();

Expand All @@ -53,35 +74,33 @@ fn main() -> Result<()> {
match read()? {
Event::Key(KeyEvent { code, modifiers }) => {
match code {
KeyCode::Char(c) if modifiers == KeyModifiers::CONTROL && c == 'd' => {
stdout.queue(MoveToNextLine(1))?.queue(Print("exit"))?;
break 'repl;
}
KeyCode::Char(c) => {
if modifiers == KeyModifiers::CONTROL && c == 'd' {
stdout.queue(MoveToNextLine(1))?.queue(Print("exit"))?;
break 'repl;
}
let insertion_point = buffer.get_insertion_point();
if insertion_point == buffer.get_buffer_len() {
stdout.queue(Print(c))?;
} else {
stdout
.queue(Print(c))?
.queue(Print(buffer.slice_buffer(insertion_point)))?
.queue(MoveToColumn(
insertion_point as u16 + prompt_offset + 1,
))?;
}
stdout.flush()?;
buffer.insert_char(buffer.get_insertion_point(), c);
buffer.inc_insertion_point();

buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
}
KeyCode::Backspace => {
let insertion_point = buffer.get_insertion_point();
if insertion_point == buffer.get_buffer_len() && !buffer.is_empty() {
buffer.dec_insertion_point();
// buffer.dec_insertion_point();
buffer.pop();
stdout
.queue(MoveLeft(1))?
.queue(Print(' '))?
.queue(MoveLeft(1))?;
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
} else if insertion_point < buffer.get_buffer_len()
&& !buffer.is_empty()
Expand All @@ -90,25 +109,41 @@ fn main() -> Result<()> {
let insertion_point = buffer.get_insertion_point();
buffer.remove_char(insertion_point);

stdout
.queue(MoveLeft(1))?
.queue(Print(buffer.slice_buffer(insertion_point)))?
.queue(Print(' '))?
.queue(MoveToColumn(insertion_point as u16 + prompt_offset))?;
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
insertion_point,
)?;

stdout.flush()?;
}
}
KeyCode::Delete => {
let insertion_point = buffer.get_insertion_point();
if insertion_point < buffer.get_buffer_len() && !buffer.is_empty() {
buffer.remove_char(insertion_point);
stdout
.queue(Print(buffer.slice_buffer(insertion_point)))?
.queue(Print(' '))?
.queue(MoveToColumn(insertion_point as u16 + prompt_offset))?;
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
insertion_point,
)?;

stdout.flush()?;
}
}
KeyCode::Home => {
buffer_repaint(&mut stdout, &buffer, prompt_offset, 0)?;
stdout.flush()?;
buffer.set_insertion_point(0);
}
KeyCode::End => {
let buffer_len = buffer.get_buffer_len();
buffer.set_insertion_point(buffer_len);
buffer_repaint(&mut stdout, &buffer, prompt_offset, buffer_len)?;
stdout.flush()?;
}
KeyCode::Enter => {
if buffer.get_buffer() == "exit" {
break 'repl;
Expand Down Expand Up @@ -138,31 +173,15 @@ fn main() -> Result<()> {
history_cursor += 1;
let history_entry =
history.get(history_cursor as usize).unwrap().clone();
let previous_buffer_len = buffer.get_buffer_len();
buffer.set_buffer(history_entry.clone());
let new_buffer_len = buffer.get_buffer_len();
let new_insertion_point = buffer.move_to_end();

// After changing the buffer, we also need to repaint the whole
// line.
// TODO: Centralize painting of the line?!
stdout
.queue(MoveToColumn(prompt_offset))?
.queue(Print(buffer.get_buffer()))?;

// Print over the rest of the line with spaces if the typed stuff
// was longer than the history entry length
for _ in 0..std::cmp::max(
0,
previous_buffer_len as i64 - new_buffer_len as i64,
) {
stdout.queue(Print(" "))?;
}
stdout
.queue(MoveToColumn(
new_insertion_point as u16 + prompt_offset,
))?
.flush()?;
buffer.move_to_end();
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
}
}
KeyCode::Down => {
Expand All @@ -182,27 +201,14 @@ fn main() -> Result<()> {

let previous_buffer_len = buffer.get_buffer_len();
buffer.set_buffer(new_buffer.clone());
let new_buffer_len = buffer.get_buffer_len();
let new_insertion_point = buffer.move_to_end();

// After changing the buffer, we also need to repaint the whole
// line.
// TODO: Centralize painting of the line?!
stdout
.queue(MoveToColumn(prompt_offset))?
.queue(Print(buffer.get_buffer()))?;

// Print over the rest of the line with spaces if the typed stuff
// was longer than the history entry length
for _ in 0..std::cmp::max(
0,
previous_buffer_len as i64 - new_buffer_len as i64,
) {
stdout.queue(Print(" "))?;
}
stdout
.queue(MoveToColumn(new_insertion_point as u16 + prompt_offset))?
.flush()?;
buffer.move_to_end();
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
}
KeyCode::Left => {
if buffer.get_insertion_point() > 0 {
Expand All @@ -215,8 +221,15 @@ fn main() -> Result<()> {
new_insertion_point as u16 + prompt_offset,
))?;
} else {
stdout.queue(MoveLeft(1))?;
// Start after the prompt
// Draw the string slice from 0 to the grapheme start left of insertion point
// Then, get the position on the screen
// Then draw the remainer of the buffer from above
// Finally, reset the cursor to the saved position
buffer.dec_insertion_point();
// let idx = buffer.get_grapheme_index_left();
let idx = buffer.get_insertion_point();
buffer_repaint(&mut stdout, &buffer, prompt_offset, idx)?;
}
stdout.flush()?;
}
Expand All @@ -229,8 +242,9 @@ fn main() -> Result<()> {
new_insertion_point as u16 + prompt_offset,
))?;
} else {
stdout.queue(MoveRight(1))?;
buffer.inc_insertion_point();
let idx = buffer.get_insertion_point();
buffer_repaint(&mut stdout, &buffer, prompt_offset, idx)?;
}
stdout.flush()?;
}
Expand Down