Files
FoundryOS/libk/src/util/editor.rs
T
zxq5 c8bb85364c - updated to-dos
- removed unnecessary debugging code
- moved x86_64-kernel.json to the project root
2025-02-28 03:05:10 +00:00

236 lines
7.1 KiB
Rust

use crate::drivers::io::keyboard::{KeyStroke, get_keystroke_async};
use crate::prelude::*;
use crate::resources::font::{FONT_CP850_8X16, FONT_SPLEEN_8X16, Font};
use crate::std::application::frame::Frame;
use crate::std::application::render::RenderError;
use crate::std::application::window::Window;
use crate::std::application::{Application, Error};
use crate::std::ascii::Writer;
use crate::std::maths::geometry::Vec2;
use alloc::string::ToString;
pub struct Editor {
cursor_line: usize,
cursor_col: usize,
mode: Mode,
buffer: String,
window: Window,
}
impl Default for Editor {
fn default() -> Self {
Self::new()
}
}
impl<'a> Editor {
const PADDING: usize = 8;
pub const fn new() -> Self {
Self {
cursor_line: 0,
cursor_col: 0,
mode: Mode::Nor,
buffer: String::new(),
window: Window::new(),
}
}
fn render(&'a self) -> Result<Frame<'a>, RenderError> {
let mut frame = Frame::new(&self.window);
let writer = Writer::new(Font::default());
let (width, height) = writer.font_size().into();
let mut col = 0;
let mut line = 0;
let mut scale = 1;
for ch in self.buffer.chars() {
if ch == '\n' {
line += scale;
col = 0;
scale = 1;
continue;
}
if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING {
line += scale;
col = 0;
}
writer.render_glyph(
&mut frame,
Vec2::new(col * width + Self::PADDING, line * height + Self::PADDING),
ch as u8,
scale,
)?;
col += scale;
}
writer.render_glyph(
&mut frame,
Vec2::new(
self.cursor_col * width + Self::PADDING,
self.cursor_line * height + Self::PADDING,
),
b'_',
scale,
);
Ok(frame)
}
#[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
fn get_lines(&self) -> Vec<&str> {
self.buffer.split('\n').collect::<Vec<&str>>()
}
#[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
fn move_cursor(&mut self, x: i32, y: i32) {
self.cursor_line = self
.cursor_line
.checked_add_signed(y as isize)
.unwrap_or(self.cursor_line);
self.cursor_col = self
.cursor_col
.checked_add_signed(x as isize)
.unwrap_or(self.cursor_col);
}
#[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
fn delete_char(&mut self) {
let i = self.get_char_idx();
self.buffer.remove(i);
}
#[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
fn insert_char(&mut self, c: char) {
let i = self.get_char_idx();
self.buffer.insert(i, c);
}
#[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
fn splitline(&mut self) {
let i = self.get_char_idx();
self.buffer.insert(i, '\n');
}
fn get_char_idx(&self) -> usize {
let frame = Frame::new(&self.window);
let writer = Writer::new(Font::default());
let (width, _height) = writer.font_size().into();
let mut col = 0;
let mut line = 0;
let mut scale = 1;
for (i, ch) in self.buffer.chars().enumerate() {
if ch == '\n' {
line += scale;
col = 0;
scale = 1;
continue;
}
if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING {
line += scale;
col = 0;
}
if col == self.cursor_col && line == self.cursor_line {
return i;
}
col += scale;
}
0
}
}
impl Application for Editor {
type Output = ();
async fn run(&mut self, _args: Vec<alloc::string::String>) -> Result<Self::Output, Error> {
self.window.set_dimensions(Vec2::new(1280, 800));
self.window.set_position(Vec2::new(0, 0));
self.window.open();
self.buffer = "Hello world, this is a test init. idk test \n ewntuiewi gjk gfdfg gndf ngdfgnmdfg ndfgmndfg gdfndfnkg njkdgjkndfjnkg ngnjfgnfgnfg fgn fn gfj gnfg jnfgjfngjk fgnjfgnjk jnkdgjnkdfg gfnd njkgdfgjn d fjnkgjkndfgjkndfgjn gndfjnk njkgdfng jnkfgdjknd jnfkgnjk".to_string();
loop {
if let Err(_err) = self.render().and_then(|frame| frame.render()) {
// TODO: Handle error
return Err(Error::ApplicationFailed("Rendering failed".to_string()));
}
let keystroke = get_keystroke_async().await;
match self.mode {
Mode::Nor => match keystroke {
KeyStroke::Char('i') => self.mode = Mode::Ins,
KeyStroke::Char('`') => return Ok(()),
_ => {}
},
Mode::Ins => {
match keystroke {
KeyStroke::Char(c) => {
match c {
// escape
'\x1B' => self.mode = Mode::Nor,
// 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 => {
serial_println!("Left\n");
self.move_cursor(-1, 0);
}
KeyStroke::Right => {
serial_println!("Right\n");
self.move_cursor(1, 0);
}
KeyStroke::Up => {
serial_println!("Up\n");
self.move_cursor(0, -1);
}
KeyStroke::Down => {
serial_println!("Down\n");
self.move_cursor(0, 1);
}
KeyStroke::None => {}
_ => {}
}
}
}
}
}
}
pub enum Mode {
Nor,
Ins,
}
impl core::fmt::Display for Mode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Nor => write!(f, "Normal"),
Self::Ins => write!(f, "Insert"),
}
}
}