split readline into it's own module directory
This commit is contained in:
99
src/prompt/readline/line.rs
Normal file
99
src/prompt/readline/line.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
|
||||||
|
#[derive(Default,Debug)]
|
||||||
|
pub struct LineBuf {
|
||||||
|
pub buffer: Vec<char>,
|
||||||
|
cursor: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineBuf {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
pub fn count_lines(&self) -> usize {
|
||||||
|
self.buffer.iter().filter(|&&c| c == '\n').count()
|
||||||
|
}
|
||||||
|
pub fn cursor(&self) -> usize {
|
||||||
|
self.cursor
|
||||||
|
}
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.buffer.clear();
|
||||||
|
self.cursor = 0;
|
||||||
|
}
|
||||||
|
pub fn insert_at_cursor(&mut self, ch: char) {
|
||||||
|
self.buffer.insert(self.cursor, ch);
|
||||||
|
self.move_cursor_right();
|
||||||
|
}
|
||||||
|
pub fn backspace_at_cursor(&mut self) {
|
||||||
|
assert!(self.cursor <= self.buffer.len());
|
||||||
|
if self.buffer.is_empty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.buffer.remove(self.cursor.saturating_sub(1));
|
||||||
|
self.move_cursor_left();
|
||||||
|
}
|
||||||
|
pub fn del_at_cursor(&mut self) {
|
||||||
|
assert!(self.cursor <= self.buffer.len());
|
||||||
|
if self.buffer.is_empty() || self.cursor == self.buffer.len() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.buffer.remove(self.cursor);
|
||||||
|
}
|
||||||
|
pub fn move_cursor_left(&mut self) {
|
||||||
|
self.cursor = self.cursor.saturating_sub(1);
|
||||||
|
}
|
||||||
|
pub fn move_cursor_start(&mut self) {
|
||||||
|
self.cursor = 0;
|
||||||
|
}
|
||||||
|
pub fn move_cursor_end(&mut self) {
|
||||||
|
self.cursor = self.buffer.len();
|
||||||
|
}
|
||||||
|
pub fn move_cursor_right(&mut self) {
|
||||||
|
if self.cursor == self.buffer.len() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.cursor = self.cursor.saturating_add(1);
|
||||||
|
}
|
||||||
|
pub fn del_from_cursor(&mut self) {
|
||||||
|
self.buffer.truncate(self.cursor);
|
||||||
|
}
|
||||||
|
pub fn del_word_back(&mut self) {
|
||||||
|
if self.cursor == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let end = self.cursor;
|
||||||
|
let mut start = self.cursor;
|
||||||
|
|
||||||
|
while start > 0 && self.buffer[start - 1].is_whitespace() {
|
||||||
|
start -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while start > 0 && !self.buffer[start - 1].is_whitespace() {
|
||||||
|
start -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.buffer.drain(start..end);
|
||||||
|
self.cursor = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn strip_ansi_codes(s: &str) -> String {
|
||||||
|
let mut out = String::with_capacity(s.len());
|
||||||
|
let mut chars = s.chars().peekable();
|
||||||
|
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
if c == '\x1b' && chars.peek() == Some(&'[') {
|
||||||
|
// Skip over the escape sequence
|
||||||
|
chars.next(); // consume '['
|
||||||
|
while let Some(&ch) = chars.peek() {
|
||||||
|
if ch.is_ascii_lowercase() || ch.is_ascii_uppercase() {
|
||||||
|
chars.next(); // consume final letter
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chars.next(); // consume intermediate characters
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
use std::{arch::asm, os::fd::BorrowedFd};
|
use std::{arch::asm, os::fd::BorrowedFd};
|
||||||
|
|
||||||
|
use line::{strip_ansi_codes, LineBuf};
|
||||||
use nix::{libc::STDIN_FILENO, sys::termios::{self, Termios}, unistd::read};
|
use nix::{libc::STDIN_FILENO, sys::termios::{self, Termios}, unistd::read};
|
||||||
|
use term::Terminal;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use crate::{libsh::{error::ShResult, sys::sh_quit}, prelude::*};
|
use crate::{libsh::{error::ShResult, sys::sh_quit}, prelude::*};
|
||||||
|
pub mod term;
|
||||||
|
pub mod line;
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
Char(char),
|
Char(char),
|
||||||
Enter,
|
Enter,
|
||||||
Backspace,
|
Backspace,
|
||||||
|
Delete,
|
||||||
Esc,
|
Esc,
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
@@ -64,112 +69,10 @@ pub enum Motion {
|
|||||||
|
|
||||||
impl EditAction {
|
impl EditAction {
|
||||||
pub fn is_return(&self) -> bool {
|
pub fn is_return(&self) -> bool {
|
||||||
if let Self::Return = self {
|
matches!(self, Self::Return)
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Terminal {
|
|
||||||
stdin: RawFd,
|
|
||||||
stdout: RawFd,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Terminal {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
assert!(isatty(0).unwrap());
|
|
||||||
Self {
|
|
||||||
stdin: 0,
|
|
||||||
stdout: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn raw_mode() -> termios::Termios {
|
|
||||||
// Get the current terminal attributes
|
|
||||||
let orig_termios = unsafe { termios::tcgetattr(BorrowedFd::borrow_raw(STDIN_FILENO)).expect("Failed to get terminal attributes") };
|
|
||||||
|
|
||||||
// Make a mutable copy
|
|
||||||
let mut raw = orig_termios.clone();
|
|
||||||
|
|
||||||
// Apply raw mode flags
|
|
||||||
termios::cfmakeraw(&mut raw);
|
|
||||||
|
|
||||||
// Set the attributes immediately
|
|
||||||
unsafe { termios::tcsetattr(BorrowedFd::borrow_raw(STDIN_FILENO), termios::SetArg::TCSANOW, &raw) }
|
|
||||||
.expect("Failed to set terminal to raw mode");
|
|
||||||
|
|
||||||
// Return original attributes so they can be restored later
|
|
||||||
orig_termios
|
|
||||||
}
|
|
||||||
pub fn restore_termios(termios: Termios) {
|
|
||||||
unsafe { termios::tcsetattr(BorrowedFd::borrow_raw(STDIN_FILENO), termios::SetArg::TCSANOW, &termios) }
|
|
||||||
.expect("Failed to restore terminal settings");
|
|
||||||
}
|
|
||||||
pub fn with_raw_mode<F: FnOnce() -> R,R>(func: F) -> R {
|
|
||||||
let saved = Self::raw_mode();
|
|
||||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func));
|
|
||||||
Self::restore_termios(saved);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => std::panic::resume_unwind(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn read_byte(&self, buf: &mut [u8]) -> usize {
|
|
||||||
Self::with_raw_mode(|| {
|
|
||||||
let ret: usize;
|
|
||||||
unsafe {
|
|
||||||
let buf_ptr = buf.as_mut_ptr();
|
|
||||||
let len = buf.len();
|
|
||||||
asm! (
|
|
||||||
"syscall",
|
|
||||||
in("rax") 0,
|
|
||||||
in("rdi") self.stdin,
|
|
||||||
in("rsi") buf_ptr,
|
|
||||||
in("rdx") len,
|
|
||||||
lateout("rax") ret,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn write_bytes(&self, buf: &[u8]) {
|
|
||||||
Self::with_raw_mode(|| {
|
|
||||||
let _ret: usize;
|
|
||||||
unsafe {
|
|
||||||
let buf_ptr = buf.as_ptr();
|
|
||||||
let len = buf.len();
|
|
||||||
asm!(
|
|
||||||
"syscall",
|
|
||||||
in("rax") 1,
|
|
||||||
in("rdi") self.stdout,
|
|
||||||
in("rsi") buf_ptr,
|
|
||||||
in("rdx") len,
|
|
||||||
lateout("rax") _ret,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pub fn write(&self, s: &str) {
|
|
||||||
self.write_bytes(s.as_bytes());
|
|
||||||
}
|
|
||||||
pub fn writeln(&self, s: &str) {
|
|
||||||
self.write(s);
|
|
||||||
self.write_bytes(b"\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Terminal {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default,Debug)]
|
#[derive(Default,Debug)]
|
||||||
pub struct FernReader {
|
pub struct FernReader {
|
||||||
@@ -215,17 +118,17 @@ impl FernReader {
|
|||||||
self.term.write_bytes(b"\r\n");
|
self.term.write_bytes(b"\r\n");
|
||||||
sh_quit(code)
|
sh_quit(code)
|
||||||
}
|
}
|
||||||
EditAction::ClearTerm => todo!(),
|
EditAction::ClearTerm => self.term.clear(),
|
||||||
EditAction::ClearLine => todo!(),
|
EditAction::ClearLine => self.line.clear(),
|
||||||
EditAction::Signal(sig) => todo!(),
|
EditAction::Signal(sig) => todo!(),
|
||||||
EditAction::MoveCursorStart => self.line.move_cursor_start(),
|
EditAction::MoveCursorStart => self.line.move_cursor_start(),
|
||||||
EditAction::MoveCursorEnd => self.line.move_cursor_end(),
|
EditAction::MoveCursorEnd => self.line.move_cursor_end(),
|
||||||
EditAction::MoveCursorLeft => self.line.move_cursor_left(),
|
EditAction::MoveCursorLeft => self.line.move_cursor_left(),
|
||||||
EditAction::MoveCursorRight => self.line.move_cursor_right(),
|
EditAction::MoveCursorRight => self.line.move_cursor_right(),
|
||||||
EditAction::DelWordBack => todo!(),
|
EditAction::DelWordBack => self.line.del_word_back(),
|
||||||
EditAction::DelFromCursor => self.line.del_from_cursor(),
|
EditAction::DelFromCursor => self.line.del_from_cursor(),
|
||||||
EditAction::Backspace => todo!(),
|
EditAction::Backspace => self.line.backspace_at_cursor(),
|
||||||
EditAction::RedrawScreen => todo!(),
|
EditAction::RedrawScreen => self.term.clear(),
|
||||||
EditAction::HistNext => todo!(),
|
EditAction::HistNext => todo!(),
|
||||||
EditAction::HistPrev => todo!(),
|
EditAction::HistPrev => todo!(),
|
||||||
EditAction::InsMode(ins_action) => self.process_ins_cmd(ins_action)?,
|
EditAction::InsMode(ins_action) => self.process_ins_cmd(ins_action)?,
|
||||||
@@ -240,7 +143,7 @@ impl FernReader {
|
|||||||
match cmd {
|
match cmd {
|
||||||
InsAction::InsChar(ch) => self.line.insert_at_cursor(ch),
|
InsAction::InsChar(ch) => self.line.insert_at_cursor(ch),
|
||||||
InsAction::Backspace => self.line.backspace_at_cursor(),
|
InsAction::Backspace => self.line.backspace_at_cursor(),
|
||||||
InsAction::Delete => todo!(),
|
InsAction::Delete => self.line.del_at_cursor(),
|
||||||
InsAction::Esc => todo!(),
|
InsAction::Esc => todo!(),
|
||||||
InsAction::MoveLeft => self.line.move_cursor_left(),
|
InsAction::MoveLeft => self.line.move_cursor_left(),
|
||||||
InsAction::MoveRight => self.line.move_cursor_right(),
|
InsAction::MoveRight => self.line.move_cursor_right(),
|
||||||
@@ -258,18 +161,21 @@ impl FernReader {
|
|||||||
}
|
}
|
||||||
pub fn get_cmds(&mut self) -> Vec<EditAction> {
|
pub fn get_cmds(&mut self) -> Vec<EditAction> {
|
||||||
match self.edit_mode {
|
match self.edit_mode {
|
||||||
EditMode::Normal => todo!(),
|
EditMode::Normal => {
|
||||||
|
let keys = self.read_keys_normal_mode();
|
||||||
|
self.process_keys_normal_mode(keys)
|
||||||
|
}
|
||||||
EditMode::Insert => {
|
EditMode::Insert => {
|
||||||
let key = self.read_key().unwrap();
|
let key = self.read_key().unwrap();
|
||||||
self.process_key(key)
|
self.process_key_insert_mode(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn process_key(&mut self, key: Key) -> Vec<EditAction> {
|
pub fn read_keys_normal_mode(&mut self) -> Vec<Key> {
|
||||||
match self.edit_mode {
|
todo!()
|
||||||
EditMode::Normal => todo!(),
|
|
||||||
EditMode::Insert => self.process_key_insert_mode(key)
|
|
||||||
}
|
}
|
||||||
|
pub fn process_keys_normal_mode(&mut self, keys: Vec<Key>) -> Vec<EditAction> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
pub fn process_key_insert_mode(&mut self, key: Key) -> Vec<EditAction> {
|
pub fn process_key_insert_mode(&mut self, key: Key) -> Vec<EditAction> {
|
||||||
match key {
|
match key {
|
||||||
@@ -282,6 +188,9 @@ impl FernReader {
|
|||||||
Key::Backspace => {
|
Key::Backspace => {
|
||||||
vec![EditAction::InsMode(InsAction::Backspace)]
|
vec![EditAction::InsMode(InsAction::Backspace)]
|
||||||
}
|
}
|
||||||
|
Key::Delete => {
|
||||||
|
vec![EditAction::InsMode(InsAction::Delete)]
|
||||||
|
}
|
||||||
Key::Esc => {
|
Key::Esc => {
|
||||||
vec![EditAction::InsMode(InsAction::Esc)]
|
vec![EditAction::InsMode(InsAction::Esc)]
|
||||||
}
|
}
|
||||||
@@ -379,11 +288,11 @@ impl FernReader {
|
|||||||
let line = self.pack_line();
|
let line = self.pack_line();
|
||||||
self.term.write(&line);
|
self.term.write(&line);
|
||||||
|
|
||||||
let cursor_offset = self.line.cursor + last_line_len;
|
let cursor_offset = self.line.cursor() + last_line_len;
|
||||||
self.term.write_bytes(format!("\r\x1b[{}C", cursor_offset).as_bytes());
|
self.term.write(&format!("\r\x1b[{}C", cursor_offset));
|
||||||
}
|
}
|
||||||
fn read_key(&mut self) -> Option<Key> {
|
fn read_key(&mut self) -> Option<Key> {
|
||||||
let mut buf = [0; 3];
|
let mut buf = [0; 4];
|
||||||
|
|
||||||
let n = self.term.read_byte(&mut buf);
|
let n = self.term.read_byte(&mut buf);
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@@ -397,7 +306,18 @@ impl FernReader {
|
|||||||
(b'[', b'B') => Some(Key::Down),
|
(b'[', b'B') => Some(Key::Down),
|
||||||
(b'[', b'C') => Some(Key::Right),
|
(b'[', b'C') => Some(Key::Right),
|
||||||
(b'[', b'D') => Some(Key::Left),
|
(b'[', b'D') => Some(Key::Left),
|
||||||
_ => Some(Key::Esc),
|
_ => {
|
||||||
|
flog!(WARN, "unhandled control seq: {},{}", buf[1] as char, buf[2] as char);
|
||||||
|
Some(Key::Esc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if n == 4 {
|
||||||
|
match (buf[1], buf[2], buf[3]) {
|
||||||
|
(b'[', b'3', b'~') => Some(Key::Delete),
|
||||||
|
_ => {
|
||||||
|
flog!(WARN, "unhandled control seq: {},{},{}", buf[1] as char, buf[2] as char, buf[3] as char);
|
||||||
|
Some(Key::Esc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(Key::Esc)
|
Some(Key::Esc)
|
||||||
@@ -421,68 +341,3 @@ pub enum EditMode {
|
|||||||
Insert,
|
Insert,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default,Debug)]
|
|
||||||
pub struct LineBuf {
|
|
||||||
buffer: Vec<char>,
|
|
||||||
cursor: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LineBuf {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
pub fn count_lines(&self) -> usize {
|
|
||||||
self.buffer.iter().filter(|&&c| c == '\n').count()
|
|
||||||
}
|
|
||||||
pub fn insert_at_cursor(&mut self, ch: char) {
|
|
||||||
self.buffer.insert(self.cursor, ch);
|
|
||||||
self.move_cursor_right();
|
|
||||||
}
|
|
||||||
pub fn backspace_at_cursor(&mut self) {
|
|
||||||
if self.buffer.is_empty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.buffer.remove(self.cursor.saturating_sub(1));
|
|
||||||
self.move_cursor_left();
|
|
||||||
}
|
|
||||||
pub fn move_cursor_left(&mut self) {
|
|
||||||
self.cursor = self.cursor.saturating_sub(1);
|
|
||||||
}
|
|
||||||
pub fn move_cursor_start(&mut self) {
|
|
||||||
self.cursor = 0;
|
|
||||||
}
|
|
||||||
pub fn move_cursor_end(&mut self) {
|
|
||||||
self.cursor = self.buffer.len();
|
|
||||||
}
|
|
||||||
pub fn move_cursor_right(&mut self) {
|
|
||||||
if self.cursor == self.buffer.len() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.cursor = self.cursor.saturating_add(1);
|
|
||||||
}
|
|
||||||
pub fn del_from_cursor(&mut self) {
|
|
||||||
self.buffer.truncate(self.cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn strip_ansi_codes(s: &str) -> String {
|
|
||||||
let mut out = String::with_capacity(s.len());
|
|
||||||
let mut chars = s.chars().peekable();
|
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
|
||||||
if c == '\x1b' && chars.peek() == Some(&'[') {
|
|
||||||
// Skip over the escape sequence
|
|
||||||
chars.next(); // consume '['
|
|
||||||
while let Some(&ch) = chars.peek() {
|
|
||||||
if ch.is_ascii_lowercase() || ch.is_ascii_uppercase() {
|
|
||||||
chars.next(); // consume final letter
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chars.next(); // consume intermediate characters
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
106
src/prompt/readline/term.rs
Normal file
106
src/prompt/readline/term.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use std::{arch::asm, os::fd::{BorrowedFd, RawFd}};
|
||||||
|
|
||||||
|
use nix::{libc::STDIN_FILENO, sys::termios, unistd::isatty};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Terminal {
|
||||||
|
stdin: RawFd,
|
||||||
|
stdout: RawFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Terminal {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
assert!(isatty(0).unwrap());
|
||||||
|
Self {
|
||||||
|
stdin: 0,
|
||||||
|
stdout: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn raw_mode() -> termios::Termios {
|
||||||
|
// Get the current terminal attributes
|
||||||
|
let orig_termios = unsafe { termios::tcgetattr(BorrowedFd::borrow_raw(STDIN_FILENO)).expect("Failed to get terminal attributes") };
|
||||||
|
|
||||||
|
// Make a mutable copy
|
||||||
|
let mut raw = orig_termios.clone();
|
||||||
|
|
||||||
|
// Apply raw mode flags
|
||||||
|
termios::cfmakeraw(&mut raw);
|
||||||
|
|
||||||
|
// Set the attributes immediately
|
||||||
|
unsafe { termios::tcsetattr(BorrowedFd::borrow_raw(STDIN_FILENO), termios::SetArg::TCSANOW, &raw) }
|
||||||
|
.expect("Failed to set terminal to raw mode");
|
||||||
|
|
||||||
|
// Return original attributes so they can be restored later
|
||||||
|
orig_termios
|
||||||
|
}
|
||||||
|
pub fn restore_termios(termios: termios::Termios) {
|
||||||
|
unsafe { termios::tcsetattr(BorrowedFd::borrow_raw(STDIN_FILENO), termios::SetArg::TCSANOW, &termios) }
|
||||||
|
.expect("Failed to restore terminal settings");
|
||||||
|
}
|
||||||
|
pub fn with_raw_mode<F: FnOnce() -> R,R>(func: F) -> R {
|
||||||
|
let saved = Self::raw_mode();
|
||||||
|
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func));
|
||||||
|
Self::restore_termios(saved);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => std::panic::resume_unwind(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn read_byte(&self, buf: &mut [u8]) -> usize {
|
||||||
|
Self::with_raw_mode(|| {
|
||||||
|
let ret: usize;
|
||||||
|
unsafe {
|
||||||
|
let buf_ptr = buf.as_mut_ptr();
|
||||||
|
let len = buf.len();
|
||||||
|
asm! (
|
||||||
|
"syscall",
|
||||||
|
in("rax") 0,
|
||||||
|
in("rdi") self.stdin,
|
||||||
|
in("rsi") buf_ptr,
|
||||||
|
in("rdx") len,
|
||||||
|
lateout("rax") ret,
|
||||||
|
out("rcx") _,
|
||||||
|
out("r11") _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn write_bytes(&self, buf: &[u8]) {
|
||||||
|
Self::with_raw_mode(|| {
|
||||||
|
let _ret: usize;
|
||||||
|
unsafe {
|
||||||
|
let buf_ptr = buf.as_ptr();
|
||||||
|
let len = buf.len();
|
||||||
|
asm!(
|
||||||
|
"syscall",
|
||||||
|
in("rax") 1,
|
||||||
|
in("rdi") self.stdout,
|
||||||
|
in("rsi") buf_ptr,
|
||||||
|
in("rdx") len,
|
||||||
|
lateout("rax") _ret,
|
||||||
|
out("rcx") _,
|
||||||
|
out("r11") _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn write(&self, s: &str) {
|
||||||
|
self.write_bytes(s.as_bytes());
|
||||||
|
}
|
||||||
|
pub fn writeln(&self, s: &str) {
|
||||||
|
self.write(s);
|
||||||
|
self.write_bytes(b"\r\n");
|
||||||
|
}
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.write_bytes(b"\x1b[2J\x1b[H");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Terminal {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user