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
13 changes: 8 additions & 5 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ X Fixed the scroll-off-the-bottom issue
* Editing engine
X Create the engine
X Initial commands for the engine
* Output commands from engine to the line editor
X Output commands from engine to the line editor
* UTF-8 support
X Line buffer
X Backspace/delete
* Navigation
X Navigation
* Unicode w/ joiner support for removal
X History
X Up/down history
X Home/end
* Refactor keypresses to flush at the end
* Ctrl-A, Ctrl-K, etc
X Refactor keypresses to flush at the end
X Ctrl-A, Ctrl-K, etc
* More Ctrl-??? key combinations
* Under status
* Async/parallel prompt
* Validation
* Autocompletion
* Syntax highlighting
* Hinting

* vi mode
17 changes: 9 additions & 8 deletions src/line_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,12 @@ impl LineBuffer {
}
self.insertion_point = 0;
}

// self.insertion_point -= 1;
}

pub fn get_buffer_len(&self) -> usize {
self.buffer.len()
}

pub fn slice_buffer(&self, pos: usize) -> &str {
&self.buffer[pos..]
}

pub fn insert_char(&mut self, pos: usize, c: char) {
self.buffer.insert(pos, c)
}
Expand All @@ -94,11 +88,18 @@ impl LineBuffer {
}

pub fn pop(&mut self) -> Option<char> {
self.buffer.pop()
let result = self.buffer.pop();
self.insertion_point = self.buffer.len();
result
}

pub fn clear(&mut self) {
self.buffer.clear()
self.buffer.clear();
self.insertion_point = 0;
}

pub fn clear_to_end(&mut self) {
self.buffer.truncate(self.insertion_point);
}

// pub fn get_grapheme_index_left(&self) -> usize {
Expand Down
123 changes: 45 additions & 78 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::io::{stdout, Write};

use crossterm::{
cursor::{
position, MoveLeft, MoveRight, MoveToColumn, MoveToNextLine, RestorePosition, SavePosition,
},
cursor::{position, MoveToColumn, MoveToNextLine, RestorePosition, SavePosition},
event::read,
event::{Event, KeyCode, KeyEvent, KeyModifiers},
style::{Color, Print, ResetColor, SetForegroundColor},
Expand Down Expand Up @@ -31,13 +29,17 @@ 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<()> {
fn buffer_repaint(stdout: &mut Stdout, buffer: &LineBuffer, prompt_offset: u16) -> Result<()> {
let raw_buffer = buffer.get_buffer();
let new_index = buffer.get_insertion_point();

// Repaint logic:
//
// 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

stdout.queue(MoveToColumn(prompt_offset))?;
stdout.queue(Print(&raw_buffer[0..new_index]))?;
Expand All @@ -46,6 +48,8 @@ fn buffer_repaint(
stdout.queue(Clear(ClearType::UntilNewLine))?;
stdout.queue(RestorePosition)?;

stdout.flush()?;

Ok(())
}

Expand All @@ -72,6 +76,24 @@ fn main() -> Result<()> {

'input: loop {
match read()? {
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::CONTROL,
}) => match code {
KeyCode::Char('d') => {
stdout.queue(MoveToNextLine(1))?.queue(Print("exit"))?;
break 'repl;
}
KeyCode::Char('a') => {
buffer.set_insertion_point(0);
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
KeyCode::Char('k') => {
buffer.clear_to_end();
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
_ => {}
},
Event::Key(KeyEvent { code, modifiers }) => {
match code {
KeyCode::Char(c) if modifiers == KeyModifiers::CONTROL && c == 'd' => {
Expand All @@ -82,67 +104,39 @@ fn main() -> Result<()> {
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()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
KeyCode::Backspace => {
let insertion_point = buffer.get_insertion_point();
if insertion_point == buffer.get_buffer_len() && !buffer.is_empty() {
// buffer.dec_insertion_point();
buffer.pop();
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
} else if insertion_point < buffer.get_buffer_len()
&& !buffer.is_empty()
{
buffer.dec_insertion_point();
let insertion_point = buffer.get_insertion_point();
buffer.remove_char(insertion_point);

buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
insertion_point,
)?;

stdout.flush()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
}
KeyCode::Delete => {
let insertion_point = buffer.get_insertion_point();
if insertion_point < buffer.get_buffer_len() && !buffer.is_empty() {
buffer.remove_char(insertion_point);
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
insertion_point,
)?;

stdout.flush()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
}
KeyCode::Home => {
buffer_repaint(&mut stdout, &buffer, prompt_offset, 0)?;
stdout.flush()?;
buffer.set_insertion_point(0);
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
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()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
KeyCode::Enter => {
if buffer.get_buffer() == "exit" {
Expand Down Expand Up @@ -175,13 +169,7 @@ fn main() -> Result<()> {
history.get(history_cursor as usize).unwrap().clone();
buffer.set_buffer(history_entry.clone());
buffer.move_to_end();
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
}
KeyCode::Down => {
Expand All @@ -199,54 +187,33 @@ fn main() -> Result<()> {
history.get(history_cursor as usize).unwrap().clone()
};

let previous_buffer_len = buffer.get_buffer_len();
buffer.set_buffer(new_buffer.clone());
buffer.move_to_end();
buffer_repaint(
&mut stdout,
&buffer,
prompt_offset,
buffer.get_insertion_point(),
)?;
stdout.flush()?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
KeyCode::Left => {
if buffer.get_insertion_point() > 0 {
// If the ALT modifier is set, we want to jump words for more
// natural editing. Jumping words basically means: move to next
// whitespace in the given direction.
if modifiers == KeyModifiers::ALT {
let new_insertion_point = buffer.move_word_left();
stdout.queue(MoveToColumn(
new_insertion_point as u16 + prompt_offset,
))?;
buffer.move_word_left();
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
} else {
// 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)?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
stdout.flush()?;
}
}
KeyCode::Right => {
if buffer.get_insertion_point() < buffer.get_buffer_len() {
if modifiers == KeyModifiers::ALT {
let new_insertion_point = buffer.move_word_right();
stdout.queue(MoveToColumn(
new_insertion_point as u16 + prompt_offset,
))?;
buffer.move_word_right();
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
} else {
buffer.inc_insertion_point();
let idx = buffer.get_insertion_point();
buffer_repaint(&mut stdout, &buffer, prompt_offset, idx)?;
buffer_repaint(&mut stdout, &buffer, prompt_offset)?;
}
stdout.flush()?;
}
}
_ => {}
Expand Down