implemented a custom text editor (but still no fs to use it with 😭)
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![feature(async_closure)]
|
||||
#![feature(inherent_associated_types)]
|
||||
#![feature(iter_advance_by)]
|
||||
|
||||
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
|
||||
@@ -51,7 +51,9 @@ pub enum KeyStroke {
|
||||
Up,
|
||||
Down,
|
||||
None,
|
||||
Enter
|
||||
Enter,
|
||||
Escape,
|
||||
Del
|
||||
}
|
||||
|
||||
impl KeyStroke {
|
||||
@@ -71,12 +73,37 @@ impl KeyStroke {
|
||||
KeyCode::ArrowUp => KeyStroke::Up,
|
||||
KeyCode::ArrowDown => KeyStroke::Down,
|
||||
KeyCode::Enter => KeyStroke::Enter,
|
||||
KeyCode::Escape => KeyStroke::Escape,
|
||||
KeyCode::Delete => KeyStroke::Del,
|
||||
_ => KeyStroke::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl core::fmt::Display for KeyStroke {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
KeyStroke::Char(c) => write!(f, "{}", c),
|
||||
KeyStroke::Ctrl => write!(f, "CTRL"),
|
||||
KeyStroke::RCtrl => write!(f, "RCtrl"),
|
||||
KeyStroke::Alt => write!(f, "ALT"),
|
||||
KeyStroke::RAlt => write!(f, "RAlt"),
|
||||
KeyStroke::Shift => write!(f, "SHIFT"),
|
||||
KeyStroke::RShift => write!(f, "RShift"),
|
||||
KeyStroke::Meta => write!(f, "META"),
|
||||
KeyStroke::RMeta => write!(f, "RMeta"),
|
||||
KeyStroke::Backspace => write!(f, "BACKSPACE"),
|
||||
KeyStroke::Left => write!(f, "LEFT"),
|
||||
KeyStroke::Right => write!(f, "RIGHT"),
|
||||
KeyStroke::Up => write!(f, "UP"),
|
||||
KeyStroke::Down => write!(f, "DOWN"),
|
||||
KeyStroke::Enter => write!(f, "ENTER"),
|
||||
KeyStroke::Escape => write!(f, "ESCAPE"),
|
||||
KeyStroke::None => write!(f, "NONE"),
|
||||
KeyStroke::Del => write!(f, "DEL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardHandler {
|
||||
pub fn new() -> KeyboardHandler {
|
||||
@@ -135,7 +162,6 @@ impl KeyboardHandler {
|
||||
}
|
||||
},
|
||||
DecodedKey::RawKey(key) => {
|
||||
print!("{:?}", key);
|
||||
match KeyStroke::from_keycode(key) {
|
||||
KeyStroke::None => (),
|
||||
key => return Some(key)
|
||||
|
||||
@@ -90,6 +90,10 @@ impl Display {
|
||||
RENDERER.lock().application_mode();
|
||||
Display
|
||||
}
|
||||
|
||||
pub fn mv_cursor(&self, x: u8, y: u8) -> Result<(), RenderError> {
|
||||
RENDERER.lock().cursor_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
|
||||
@@ -72,6 +72,10 @@ impl<T: Num + ToPrimitive> Position<T> {
|
||||
Position { x, y }
|
||||
}
|
||||
|
||||
pub fn zero() -> Position<T> {
|
||||
Position { x: T::zero(), y: T::zero() }
|
||||
}
|
||||
|
||||
pub fn into_usize(self) -> Result<Position<usize>, ()> {
|
||||
Ok(Position {
|
||||
x: self.x.to_usize().ok_or(())?,
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
use crate::{serial_println, std};
|
||||
use crate::std::application::{self, Application};
|
||||
use crate::std::io::{Color, ColorCode, Display, KeyStroke, Screen};
|
||||
use crate::std::render::{ColouredChar, Frame, Position, RenderError};
|
||||
use crate::user::lib::libgui::cg_core::CgComponent;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::{string::{String, ToString}, vec::Vec, boxed::Box};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
|
||||
pub struct Editor {
|
||||
buffer: Vec<Vec<char>>, // outer vec is the line, inner is col;
|
||||
cursor_pos: Position<i32>,
|
||||
offset_pos: Position<i32>,
|
||||
command: String,
|
||||
mode: Mode,
|
||||
unsaved: bool,
|
||||
display: Display,
|
||||
lineno_width: i32,
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Normal,
|
||||
Insert,
|
||||
Command,
|
||||
Diff // TODO
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Mode {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Mode::Normal => write!(f, "Normal"),
|
||||
Mode::Insert => write!(f, "Insert"),
|
||||
Mode::Command => write!(f, "Commnd"),
|
||||
Mode::Diff => write!(f, "Diff "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Editor {
|
||||
fn move_cursor(&mut self, dx: i32, dy: i32) {
|
||||
if dy != 0
|
||||
&& self.cursor_pos.y + dy >= 0
|
||||
&& self.cursor_pos.y + dy <= self.buffer.len() as i32
|
||||
{
|
||||
self.cursor_pos.y += dy;
|
||||
let line_width = self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32;
|
||||
if self.cursor_pos.x > line_width {
|
||||
self.cursor_pos.x = line_width;
|
||||
}
|
||||
} else if self.cursor_pos.x + dx < 0 {
|
||||
if self.cursor_pos.y - 1 >= 0 {
|
||||
self.cursor_pos.y -= 1;
|
||||
self.cursor_pos.x = self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32;
|
||||
}
|
||||
} else if self.cursor_pos.x + dx > self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32 {
|
||||
if self.cursor_pos.y + 1 <= self.buffer.len() as i32 {
|
||||
self.cursor_pos.x = 0;
|
||||
self.cursor_pos.y += 1;
|
||||
}
|
||||
} else if dx != 0 {
|
||||
self.cursor_pos.x += dx;
|
||||
}
|
||||
|
||||
serial_println!("cursor: {} {} offset: {} {} ", self.cursor_pos.x, self.cursor_pos.y, self.offset_pos.x, self.offset_pos.y);
|
||||
|
||||
while self.cursor_pos.x + 3 + (self.lineno_width + 2) > 80 + self.offset_pos.x {
|
||||
self.offset_pos.x += 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.x - 3 < self.offset_pos.x && self.offset_pos.x - 3 >= 0 {
|
||||
self.offset_pos.x -= 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.y + 3 > self.offset_pos.y + 25 {
|
||||
self.offset_pos.y += 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.y - 3 < self.offset_pos.y && self.offset_pos.y - 3 >= 0 {
|
||||
self.offset_pos.y -= 1;
|
||||
}
|
||||
|
||||
serial_println!(
|
||||
"moving cursor to {}, {}",
|
||||
(self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2) as u8,
|
||||
(self.cursor_pos.y - self.offset_pos.y) as u8
|
||||
);
|
||||
|
||||
// print all the values below
|
||||
serial_println!("offset: {}, {}", self.offset_pos.x, self.offset_pos.y);
|
||||
serial_println!("cursor: {}, {}", self.cursor_pos.x, self.cursor_pos.y);
|
||||
serial_println!("line width: {}", self.lineno_width + 2);
|
||||
|
||||
self.display.mv_cursor(
|
||||
(self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2) as u8,
|
||||
(self.cursor_pos.y - self.offset_pos.y) as u8
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn delete_char(&mut self) {
|
||||
self.unsaved = true;
|
||||
// if the cursor is at the end of the line
|
||||
if self.cursor_pos.x == self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32 {
|
||||
|
||||
if self.cursor_pos.y + 1 == self.buffer.len() as i32 {
|
||||
return;
|
||||
}
|
||||
|
||||
let old_line = self.buffer[self.cursor_pos.y as usize + 1].clone();
|
||||
self.buffer[self.cursor_pos.y as usize].extend(&old_line);
|
||||
self.buffer.remove(self.cursor_pos.y as usize + 1);
|
||||
} else {
|
||||
self.buffer[self.cursor_pos.y as usize].remove(self.cursor_pos.x as usize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn splitline(&mut self) {
|
||||
self.unsaved = true;
|
||||
|
||||
if let Some(_) = self.buffer.get(self.cursor_pos.y as usize) {
|
||||
let first_half = self.buffer[self.cursor_pos.y as usize][..self.cursor_pos.x as usize].to_vec();
|
||||
let second_half = self.buffer[self.cursor_pos.y as usize][self.cursor_pos.x as usize..].to_vec();
|
||||
|
||||
self.buffer[self.cursor_pos.y as usize] = first_half;
|
||||
self.buffer.insert(self.cursor_pos.y as usize + 1, second_half);
|
||||
} else {
|
||||
self.buffer.push(Vec::new());
|
||||
}
|
||||
|
||||
self.move_cursor(1, 0);
|
||||
}
|
||||
|
||||
fn insert_char(&mut self, c: char) {
|
||||
self.unsaved = true;
|
||||
|
||||
if let Some(line) = self.buffer.get_mut(self.cursor_pos.y as usize) {
|
||||
line.insert(self.cursor_pos.x as usize, c);
|
||||
} else {
|
||||
self.buffer.push(Vec::new());
|
||||
self.buffer.get_mut(self.cursor_pos.y as usize).unwrap().push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Editor {
|
||||
fn to_string(&self) -> String {
|
||||
self.buffer.iter().map(|line| line.iter().collect::<String>()).collect::<Vec<String>>().join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Application for Editor {
|
||||
fn new() -> Editor {
|
||||
Editor {
|
||||
buffer: Vec::new(),
|
||||
cursor_pos: Position::zero(),
|
||||
offset_pos: Position::zero(),
|
||||
command: String::new(),
|
||||
mode: Mode::Normal,
|
||||
unsaved: false,
|
||||
display: Display::borrow(),
|
||||
lineno_width: 0
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), application::Error> {
|
||||
|
||||
// if let Some(s) = args.get(0) {
|
||||
// self.buffer = s.lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>()
|
||||
// }
|
||||
|
||||
self.buffer = String::from("
|
||||
/$$ /$$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$ /$$ /$$
|
||||
/$$/|_____ $$ | $$ / $$ /$$__ $$| $$____/ /$$/| $$| $$
|
||||
/$$/ /$$/ | $$/ $$/| $$ \\ $$| $$ /$$/ \\ $$\\ $$
|
||||
/$$/ /$$/ \\ $$$$/ | $$ | $$| $$$$$$$ /$$/ \\ $$\\ $$
|
||||
| $$ /$$/ >$$ $$ | $$ | $$|_____ $$ /$$/ /$$/ /$$/
|
||||
\\ $$ /$$/ /$$/\\ $$| $$/$$ $$ /$$ \\ $$ /$$/ /$$/ /$$/
|
||||
\\ $$ /$$$$$$$$| $$ \\ $$| $$$$$$/| $$$$$$//$$/ /$$/ /$$/
|
||||
\\__/|________/|__/ |__/ \\____ $$$ \\______/|__/ |__/ |__/
|
||||
\\__/
|
||||
").lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>();
|
||||
|
||||
|
||||
|
||||
loop {
|
||||
// start by rendering the screen
|
||||
self.lineno_width = self.buffer.len().to_string().len() as i32;
|
||||
self.render().unwrap().write_to_screen().unwrap();
|
||||
|
||||
// wait for a keyboard input
|
||||
let keystroke = std::io::Stdin::keystroke().await;
|
||||
|
||||
match self.mode {
|
||||
Mode::Normal => {
|
||||
match keystroke {
|
||||
KeyStroke::Char('i') => self.mode = Mode::Insert,
|
||||
KeyStroke::Char(':') => self.mode = Mode::Command,
|
||||
KeyStroke::Char('d') => self.mode = Mode::Diff,
|
||||
KeyStroke::Char('`') => {
|
||||
// TODO: End terminal session
|
||||
// ncurses::endwin();
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Mode::Insert => {
|
||||
match keystroke {
|
||||
KeyStroke::Enter => {
|
||||
// TODO: newline function
|
||||
},
|
||||
KeyStroke::Char(c) => {
|
||||
match c {
|
||||
// escape
|
||||
'\x1B' => self.mode = Mode::Normal,
|
||||
// delete
|
||||
'\x7F' => self.delete_char(),
|
||||
// backspace
|
||||
'\x08' => {
|
||||
self.move_cursor(-1, 0);
|
||||
self.delete_char();
|
||||
},
|
||||
// enter
|
||||
'\n' => self.splitline(),
|
||||
_ => {
|
||||
self.insert_char(c);
|
||||
self.move_cursor(1, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
KeyStroke::Left => {
|
||||
self.move_cursor(-1, 0);
|
||||
},
|
||||
KeyStroke::Right => {
|
||||
self.move_cursor(1, 0);
|
||||
},
|
||||
KeyStroke::Up => {
|
||||
self.move_cursor(0, -1);
|
||||
},
|
||||
KeyStroke::Down => {
|
||||
self.move_cursor(0, 1);
|
||||
},
|
||||
KeyStroke::None => {
|
||||
serial_println!("none");
|
||||
},
|
||||
_ => {
|
||||
serial_println!("other");
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Command => {
|
||||
match keystroke {
|
||||
KeyStroke::Enter => {
|
||||
// TODO: execute command
|
||||
},
|
||||
KeyStroke::Char(c) => {
|
||||
if c == '\x1B' {
|
||||
self.mode = Mode::Normal;
|
||||
self.command.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
self.command.push(c);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Mode::Diff => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for Editor {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut frame = Frame::new(Position::zero(), Position::new(80, 25))?;
|
||||
let width = self.lineno_width as usize;
|
||||
let linecolour = ColorCode::new(Color::Cyan, Color::Black);
|
||||
|
||||
for (i, line) in (self.offset_pos.y..self.offset_pos.y + 24).enumerate() {
|
||||
if line >= self.buffer.len() as i32 {
|
||||
break;
|
||||
}
|
||||
|
||||
// render the line numbers on the left hand side of the screen
|
||||
let line_num = format!("{:width$} │", line + 1);
|
||||
for (j, c) in line_num.chars().enumerate() {
|
||||
frame.write(Position::new(j, i), ColouredChar::coloured(c, linecolour))?;
|
||||
}
|
||||
|
||||
let line = self.buffer[line as usize].iter().collect::<String>();
|
||||
|
||||
for (j, c) in line.chars().skip(self.offset_pos.x as usize).take(80 - (width + 2)).enumerate() {
|
||||
frame.write(Position::new(j + width + 2, i), ColouredChar::new(c))?;
|
||||
}
|
||||
}
|
||||
|
||||
// render the toolbar
|
||||
|
||||
// render mode (8 chars)
|
||||
let mode = format!("[{}]", self.mode);
|
||||
|
||||
// render unsaved (10 chars)
|
||||
let unsaved = String::from(if self.unsaved { "[Unsaved!]" } else { "" });
|
||||
|
||||
// line and col (variable width)
|
||||
let line_and_col = format!("[{}:{}] ", self.cursor_pos.y + 1, self.cursor_pos.x + 1);
|
||||
|
||||
// write to screen
|
||||
let toolbar = line_and_col + " " + &mode + " " + &unsaved;
|
||||
|
||||
for (i, c) in toolbar.chars().enumerate() {
|
||||
frame.write(Position::new(i, 24), ColouredChar::new(c))?;
|
||||
}
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::std::application::{Application, Error};
|
||||
use crate::std::render::{ColouredChar, Dimensions, Frame, RenderError, ColorCode};
|
||||
use crate::std::random::Random;
|
||||
use crate::system::std::render;
|
||||
use super::super::super::lib::coords::{Position, Direction};
|
||||
use super::super::super::lib::geometry::{Position, Direction};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Gamemode {
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod crystalfetch;
|
||||
pub mod rickroll;
|
||||
pub mod shell;
|
||||
pub mod tasks;
|
||||
pub mod editor;
|
||||
mod gigachad_detector;
|
||||
|
||||
//mod shellrewrite;
|
||||
|
||||
@@ -156,6 +156,11 @@ async fn exec() -> Result<(), Error> {
|
||||
detector.run(args).await?;
|
||||
}
|
||||
|
||||
"editor" => {
|
||||
let mut editor = editor::Editor::new();
|
||||
editor.run(args).await?;
|
||||
}
|
||||
|
||||
"wait" => {
|
||||
if args.len() != 1 {
|
||||
return Err(Error::CommandFailed("exactly one argument must be provided".to_string()))
|
||||
|
||||
@@ -118,6 +118,12 @@ impl Position {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Position {
|
||||
Position {
|
||||
x: 0,
|
||||
y: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Add for Position {
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// pub mod libgui_old_archive;
|
||||
pub mod coords;
|
||||
pub mod geometry;
|
||||
pub mod libgui;
|
||||
|
||||
Reference in New Issue
Block a user