diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ee79c9d..d8628b001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,18 @@ # Changelog ## Unreleased +- Improve UTF-8 support (#267) +- Bump acpi from 4.0.0 to 4.1.0 (#265) - Add shell redirections (#262) +- Bump linked_list_allocator from 0.9.0 to 0.9.1 (#256) - Add calc command (#263) - Add website (#261) - Fix VGA issues with real hardware (#258) - Add rust binaries support (#255) - Add dynamical disk information (#252) +- Remove array-macro dependency (#253) - Add spawn syscall (#251) +- Bump x86_64 from 0.14.5 to 0.14.6 (#247) - Add ELF loader (#248) - Add basic userspace (#228) - Bump acpi from 3.1.0 to 4.0.0 (#243) diff --git a/src/api/console.rs b/src/api/console.rs index 8b6ecb88b..6be934e75 100644 --- a/src/api/console.rs +++ b/src/api/console.rs @@ -81,7 +81,9 @@ fn color_to_bg(name: &str) -> Option { pub fn is_printable(c: char) -> bool { if cfg!(feature = "video") { - c.is_ascii() && sys::vga::is_printable(c as u8) + // Check if the char can be converted to ASCII or Extended ASCII before + // asking the VGA driver if it's printable. + ((c as u32) < 0xFF) && sys::vga::is_printable(c as u8) } else { true // TODO } diff --git a/src/api/io.rs b/src/api/io.rs index 8db3f7542..5c45400b5 100644 --- a/src/api/io.rs +++ b/src/api/io.rs @@ -13,10 +13,11 @@ impl Stdin { } pub fn read_char(&self) -> Option { - let mut buf = vec![0; 1]; + let mut buf = vec![0; 4]; if let Some(bytes) = syscall::read(0, &mut buf) { if bytes > 0 { - return Some(buf[0] as char); + buf.resize(bytes, 0); + return Some(String::from_utf8_lossy(&buf).to_string().remove(0)); } } None diff --git a/src/api/prompt.rs b/src/api/prompt.rs index 2d766deca..ea73d510f 100644 --- a/src/api/prompt.rs +++ b/src/api/prompt.rs @@ -10,7 +10,7 @@ pub struct Prompt { pub history: History, offset: usize, // Offset line by the length of the prompt string cursor: usize, - line: String, + line: Vec, // UTF-32 } impl Prompt { @@ -20,7 +20,7 @@ impl Prompt { history: History::new(), offset: 0, cursor: 0, - line: String::new(), + line: Vec::with_capacity(80), } } @@ -28,7 +28,7 @@ impl Prompt { print!("{}", prompt); self.offset = offset_from_prompt(prompt); self.cursor = self.offset; - self.line = String::new(); + self.line = Vec::with_capacity(80); let mut parser = Parser::new(); while let Some(c) = io::stdin().read_char() { match c { @@ -44,11 +44,11 @@ impl Prompt { self.update_completion(); self.update_history(); println!(); - return Some(self.line.clone()); + return Some(self.line.iter().collect()); }, c => { - if c.is_ascii() { - parser.advance(self, c as u8); + for b in c.to_string().as_bytes() { + parser.advance(self, *b); } } } @@ -59,15 +59,15 @@ impl Prompt { fn update_history(&mut self) { if let Some(i) = self.history.pos { - self.line = self.history.entries[i].clone(); + self.line = self.history.entries[i].chars().collect(); self.history.pos = None; } } fn update_completion(&mut self) { if let Some(i) = self.completion.pos { - let complete = self.completion.entries[i].clone(); - self.line.push_str(&complete); + let mut complete: Vec = self.completion.entries[i].chars().collect(); + self.line.append(&mut complete); self.cursor += complete.len(); self.completion.pos = None; self.completion.entries = Vec::new(); @@ -83,7 +83,7 @@ impl Prompt { self.update_completion(); return; } - let bs = self.completion.entries[pos].len(); + let bs = self.completion.entries[pos].chars().count(); if pos + 1 < n { (bs, pos + 1) } else { @@ -91,7 +91,8 @@ impl Prompt { } }, None => { - self.completion.entries = (self.completion.completer)(&self.line); + let line: String = self.line.iter().collect(); + self.completion.entries = (self.completion.completer)(&line); if !self.completion.entries.is_empty() { (0, 0) } else { @@ -112,14 +113,14 @@ impl Prompt { return; } let (bs, i) = match self.history.pos { - Some(i) => (self.history.entries[i].len(), cmp::max(i, 1) - 1), + Some(i) => (self.history.entries[i].chars().count(), cmp::max(i, 1) - 1), None => (self.line.len(), n - 1), }; let line = &self.history.entries[i]; let blank = ' '.to_string().repeat((self.offset + bs) - self.cursor); let erase = '\x08'.to_string().repeat(bs); print!("{}{}{}", blank, erase, line); - self.cursor = self.offset + line.len(); + self.cursor = self.offset + line.chars().count(); self.history.pos = Some(i); } @@ -130,17 +131,17 @@ impl Prompt { return; } let (bs, i) = match self.history.pos { - Some(i) => (self.history.entries[i].len(), i + 1), + Some(i) => (self.history.entries[i].chars().count(), i + 1), None => return, }; let (pos, line) = if i < n { - (Some(i), &self.history.entries[i]) + (Some(i), self.history.entries[i].clone()) } else { - (None, &self.line) + (None, self.line.iter().collect()) }; let erase = '\x08'.to_string().repeat(bs); print!("{}{}", erase, line); - self.cursor = self.offset + line.len(); + self.cursor = self.offset + line.chars().count(); self.history.pos = pos; } @@ -168,8 +169,10 @@ impl Prompt { if self.cursor < self.offset + self.line.len() { let i = self.cursor - self.offset; self.line.remove(i); - let s = &self.line[i..]; - print!("{} \x1b[{}D", s, s.len() + 1); + let s = &self.line[i..]; // UTF-32 + let n = s.len() + 1; + let s: String = s.into_iter().collect(); // UTF-8 + print!("{} \x1b[{}D", s, n); } } @@ -179,8 +182,10 @@ impl Prompt { if self.cursor > self.offset { let i = self.cursor - self.offset - 1; self.line.remove(i); - let s = &self.line[i..]; - print!("{}{} \x1b[{}D", '\x08', s, s.len() + 1); + let s = &self.line[i..]; // UTF-32 + let n = s.len() + 1; + let s: String = s.into_iter().collect(); // UTF-8 + print!("{}{} \x1b[{}D", '\x08', s, n); self.cursor -= 1; } } @@ -189,11 +194,15 @@ impl Prompt { self.update_completion(); self.update_history(); if console::is_printable(c) { + let c = (c as u8) as char; let i = self.cursor - self.offset; self.line.insert(i, c); - let s = &self.line[i..]; - print!("{} \x1b[{}D", s, s.len()); + let s = &self.line[i..]; // UTF-32 + let n = s.len(); + let s: String = s.into_iter().collect(); // UTF-8 + print!("{} \x1b[{}D", s, n); self.cursor += 1; + } else { } } } diff --git a/src/api/regex.rs b/src/api/regex.rs index e9b91a5c0..471015eea 100644 --- a/src/api/regex.rs +++ b/src/api/regex.rs @@ -79,11 +79,11 @@ impl Regex { self.find(text).is_some() } pub fn find(&self, text: &str) -> Option<(usize, usize)> { - let vec_re: Vec = self.0.chars().collect(); - let vec_text: Vec = text.chars().collect(); + let text: Vec = text.chars().collect(); // UTF-32 + let re: Vec = self.0.chars().collect(); // UTF-32 let mut start = 0; let mut end = 0; - if is_match(&vec_re[..], &vec_text[..], &mut start, &mut end) { + if is_match(&re[..], &text[..], &mut start, &mut end) { Some((start, end)) } else { None diff --git a/src/sys/console.rs b/src/sys/console.rs index aa52a0892..d880fa6cb 100644 --- a/src/sys/console.rs +++ b/src/sys/console.rs @@ -24,7 +24,7 @@ impl Console { impl FileIO for Console { fn read(&mut self, buf: &mut [u8]) -> Result { - let mut s = if buf.len() == 1 { + let mut s = if buf.len() == 4 { read_char().to_string() } else { read_line() @@ -105,12 +105,13 @@ pub fn key_handle(key: char) { if is_echo_enabled() { let n = match c { ETX_KEY | EOT_KEY | ESC_KEY => 2, - _ => c.len_utf8(), + _ => if (c as u32) < 0xFF { 1 } else { c.len_utf8() }, }; print_fmt(format_args!("{}", BS_KEY.to_string().repeat(n))); } } } else { + let key = if (key as u32) < 0xFF { (key as u8) as char } else { key }; stdin.push(key); if is_echo_enabled() { match key { diff --git a/src/usr/editor.rs b/src/usr/editor.rs index 6e8de47e5..c9d1c470d 100644 --- a/src/usr/editor.rs +++ b/src/usr/editor.rs @@ -1,7 +1,6 @@ use crate::{sys, usr}; -use crate::api::fs; +use crate::api::{console, fs, io}; use crate::api::console::Style; -use crate::api::io; use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -88,7 +87,7 @@ impl Editor { fn print_editing_status(&mut self) { let max = 50; let mut path = self.pathname.clone(); - if self.pathname.len() > max { + if self.pathname.chars().count() > max { path.truncate(max - 3); path.push_str("..."); } @@ -99,7 +98,7 @@ impl Editor { let n = y * 100 / self.lines.len(); let end = format!("{},{} {:3}%", y, x, n); - let width = self.cols() - start.len(); + let width = self.cols() - start.chars().count(); let status = format!("{}{:>width$}", start, end, width = width); self.print_status(&status, "LightGray"); @@ -119,23 +118,23 @@ impl Editor { // Render line into a row of the screen, or an empty row when past eof let line = if y < self.lines.len() { &self.lines[y] } else { "" }; - let mut row = format!("{:cols$}", line, cols = self.dx); + let mut row: Vec = format!("{:cols$}", line, cols = self.dx).chars().collect(); let n = self.dx + self.cols(); - if row.len() > n { + let mut after: Vec = if row.len() > n { row.truncate(n - 1); - row.push_str(&truncated_line_indicator()); + truncated_line_indicator() } else { - row.push_str(&" ".repeat(n - row.len())); - } - row[self.dx..].to_string() + " ".repeat(n - row.len()) + }.chars().collect(); + row.append(&mut after); + row[self.dx..].iter().collect() } fn render_char(&self, c: char) -> Option { match c { - '!'..='~' => Some(c.to_string()), // graphic char - ' ' => Some(" ".to_string()), - '\t' => Some(" ".repeat(self.config.tab_size)), - _ => None, + '\t' => Some(" ".repeat(self.config.tab_size)), + c if console::is_printable(c) => Some(c.to_string()), + _ => None, } } @@ -180,8 +179,12 @@ impl Editor { return res; }, '\n' => { // Newline - let line = self.lines[self.dy + self.y].split_off(self.dx + self.x); - self.lines.insert(self.dy + self.y + 1, line); + let y = self.dy + self.y; + let old_line = self.lines[y].clone(); + let mut row: Vec = old_line.chars().collect(); + let new_line = row.split_off(self.dx + self.x).into_iter().collect(); + self.lines[y] = row.into_iter().collect(); + self.lines.insert(y + 1, new_line); if self.y == self.rows() - 1 { self.dy += 1; } else { @@ -217,7 +220,7 @@ impl Editor { }, 'C' if csi => { // Arrow right let line = &self.lines[self.dy + self.y]; - if line.is_empty() || self.x + self.dx >= line.len() { + if line.is_empty() || self.x + self.dx >= line.chars().count() { print!("\x1b[?25h"); // Enable cursor continue } else if self.x == self.cols() - 1 { @@ -261,22 +264,27 @@ impl Editor { self.print_screen(); }, '\x05' => { // Ctrl E -> Go to end of line - let n = self.lines[self.dy + self.y].len(); + let n = self.lines[self.dy + self.y].chars().count(); let w = self.cols(); self.x = n % w; self.dx = w * (n / w); self.print_screen(); }, '\x08' => { // Backspace + let y = self.dy + self.y; if self.dx + self.x > 0 { // Remove char from line - self.lines[self.dy + self.y].remove(self.dx + self.x - 1); + + let mut row: Vec = self.lines[y].chars().collect(); + row.remove(self.dx + self.x - 1); + self.lines[y] = row.into_iter().collect(); + if self.x == 0 { self.dx -= self.cols(); self.x = self.cols() - 1; self.print_screen(); } else { self.x -= 1; - let line = self.render_line(self.dy + self.y); + let line = self.render_line(y); print!("\x1b[2K\x1b[1G{}", line); } } else { // Remove newline from previous line @@ -286,14 +294,14 @@ impl Editor { } // Move cursor below the end of the previous line - let n = self.lines[self.dy + self.y - 1].len(); + let n = self.lines[y - 1].chars().count(); let w = self.cols(); self.x = n % w; self.dx = w * (n / w); // Move line to the end of the previous line - let line = self.lines.remove(self.dy + self.y); - self.lines[self.dy + self.y - 1].push_str(&line); + let line = self.lines.remove(y); + self.lines[y - 1].push_str(&line); // Move cursor up to the previous line if self.y > 0 { @@ -306,21 +314,27 @@ impl Editor { } }, '\x7f' => { // Delete - let n = self.lines[self.dy + self.y].len(); + let y = self.dy + self.y; + let n = self.lines[y].chars().count(); if self.dx + self.x >= n { // Remove newline from line - let line = self.lines.remove(self.dy + self.y + 1); - self.lines[self.dy + self.y].push_str(&line); + let line = self.lines.remove(y + 1); + self.lines[y].push_str(&line); self.print_screen(); } else { // Remove char from line - self.lines[self.dy + self.y].remove(self.dx + self.x); - let line = self.render_line(self.dy + self.y); + self.lines[y].remove(self.dx + self.x); + let line = self.render_line(y); print!("\x1b[2K\x1b[1G{}", line); } }, c => { if let Some(s) = self.render_char(c) { - self.lines[self.dy + self.y].insert_str(self.dx + self.x, &s); - self.x += s.len(); + let y = self.dy + self.y; + let mut row: Vec = self.lines[y].chars().collect(); + for c in s.chars() { + row.insert(self.dx + self.x, c); + self.x += 1; + } + self.lines[y] = row.into_iter().collect(); if self.x >= self.cols() { self.dx += self.cols(); self.x -= self.cols(); @@ -343,7 +357,7 @@ impl Editor { // Move cursor past end of line to end of line or left of the screen fn next_pos(&self, x: usize, y: usize) -> usize { - let eol = self.lines[self.dy + y].len(); + let eol = self.lines[self.dy + y].chars().count(); if eol <= self.dx + x { if eol <= self.dx { 0