aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs43
-rw-r--r--src/rope.rs99
-rw-r--r--src/term.rs24
3 files changed, 101 insertions, 65 deletions
diff --git a/src/main.rs b/src/main.rs
index 5b9d2f5..0cc9ad4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,7 +8,7 @@ use std::fs::File;
use std::io::{stderr, stdout, Write};
use std::path::{Path, PathBuf};
-fn open(file: &Path) -> Result<Rope, std::io::Error> {
+fn open(file: &Path) -> Result<Rope, Box<dyn Error>> {
let mut f = File::open(file)?;
let r = Rope::read(&mut f)?;
Ok(r)
@@ -38,10 +38,7 @@ fn line_offset(stdin: &mut Reader, line: &Rope, col: u16) -> Result<usize, Box<d
let mut lo = 0;
let mut hi = line.len() + 1;
while lo < hi {
- let mut h = lo + (hi - lo) / 2;
- if h < line.len() {
- h = line.floor_char_boundary(h);
- }
+ let h = line.floor_char_boundary(lo + (hi - lo) / 2);
write!(stdout(), "\x1b[G")?;
line.slice(0, h).print(&mut stdout())?;
let (_, c) = stdin.cursor_pos()?;
@@ -234,12 +231,12 @@ impl State {
}
fn keypress(&mut self, key: Key) -> Result<bool, Box<dyn Error>> {
- const CTRL_Q: u8 = b'Q' - b'@';
- const CTRL_S: u8 = b'S' - b'@';
- const CTRL_Y: u8 = b'Y' - b'@';
- const CTRL_Z: u8 = b'Z' - b'@';
- const BACKSPACE: u8 = b'H' - b'@';
- const OTHER_BACKSPACE: u8 = 127;
+ const CTRL_Q: char = (b'Q' - b'@') as char;
+ const CTRL_S: char = (b'S' - b'@') as char;
+ const CTRL_Y: char = (b'Y' - b'@') as char;
+ const CTRL_Z: char = (b'Z' - b'@') as char;
+ const BACKSPACE: char = (b'H' - b'@') as char;
+ const OTHER_BACKSPACE: char = '\x7f';
let inserting = self.inserting;
self.inserting = false;
@@ -446,15 +443,15 @@ impl State {
self.update_cursor_col()?;
stdout().flush()?;
}
- Key::Byte(CTRL_Q) => {
+ Key::Char(CTRL_Q) => {
return Ok(false);
}
- Key::Byte(CTRL_S) => {
+ Key::Char(CTRL_S) => {
let mut f = File::create(&self.path)?;
self.buf.print(&mut f)?;
f.sync_all()?;
}
- Key::Byte(CTRL_Z) => {
+ Key::Char(CTRL_Z) => {
if let Some(n) = self.n {
if n > 0 {
self.n = Some(n - 1);
@@ -468,7 +465,7 @@ impl State {
self.update_cursor_col()?;
stdout().flush()?;
}
- Key::Byte(CTRL_Y) => {
+ Key::Char(CTRL_Y) => {
let Some(n) = self.n else {
return Ok(true);
};
@@ -481,7 +478,7 @@ impl State {
self.update_cursor_col()?;
stdout().flush()?;
}
- Key::Byte(BACKSPACE) | Key::Byte(OTHER_BACKSPACE) => {
+ Key::Char(BACKSPACE) | Key::Char(OTHER_BACKSPACE) => {
self.insert(inserting);
if self.line_offset == 0 && self.cursor_row == 0 && self.row_start == 0 {
return Ok(true);
@@ -519,7 +516,7 @@ impl State {
self.update_cursor_col()?;
stdout().flush()?;
}
- Key::Byte(b'\r') | Key::Byte(b'\n') => {
+ Key::Char('\r') | Key::Char('\n') => {
self.insert(inserting);
let size = term::size()?;
let offset = self
@@ -529,7 +526,7 @@ impl State {
self.buf = self
.buf
.slice(0, offset)
- .concat(&Rope::new(&[b'\n']))
+ .concat(&Rope::new("\n"))
.concat(&self.buf.slice(offset, self.buf.len()));
if self.cursor_row < size.ws_row - 1 {
self.cursor_row += 1;
@@ -542,20 +539,22 @@ impl State {
write!(stdout(), "\r\n")?;
stdout().flush()?;
}
- Key::Byte(c) => {
+ Key::Char(c) => {
self.insert(inserting);
let offset = self
.buf
.line_start(self.row_start + usize::from(self.cursor_row))
+ self.line_offset;
+ let mut buf = [0; 4];
+ let s = c.encode_utf8(&mut buf);
self.buf = self
.buf
.slice(0, offset)
- .concat(&Rope::new(&[c]))
+ .concat(&Rope::new(s))
.concat(&self.buf.slice(offset, self.buf.len()));
- self.line_offset += 1;
+ self.line_offset += s.len();
self.need_repaint_line = true;
- stdout().write_all(&[c])?;
+ stdout().write_all(s.as_bytes())?;
stdout().flush()?;
}
}
diff --git a/src/rope.rs b/src/rope.rs
index 4e14d84..ab9a7bc 100644
--- a/src/rope.rs
+++ b/src/rope.rs
@@ -1,8 +1,23 @@
+use std::error::Error;
use std::io::{ErrorKind, Read, Write};
use std::rc::Rc;
const MAX_NODE_SIZE: usize = 1024;
+fn read(r: &mut dyn Read, buf: &mut [u8]) -> Result<usize, std::io::Error> {
+ loop {
+ match r.read(buf) {
+ Ok(n) => return Ok(n),
+ Err(err) => {
+ if err.kind() == ErrorKind::Interrupted {
+ continue;
+ }
+ return Err(err);
+ }
+ }
+ }
+}
+
enum Insert {
Node(Rope),
Split(Rope, Rope),
@@ -10,16 +25,16 @@ enum Insert {
#[derive(Debug, Clone)]
struct Leaf {
- unsafe_buf: Option<Rc<[u8]>>,
+ unsafe_buf: Option<Rc<str>>,
start: u16,
end: u16,
lines: u16,
}
impl Leaf {
- fn bytes(&self) -> &[u8] {
+ fn buf(&self) -> &str {
match &self.unsafe_buf {
- None => &[],
+ None => "",
Some(buf) => &buf[usize::from(self.start)..usize::from(self.end)],
}
}
@@ -44,7 +59,7 @@ pub struct Rope(Node);
impl Rope {
pub fn len(&self) -> usize {
match self {
- Rope(Node::Leaf(l)) => l.bytes().len(),
+ Rope(Node::Leaf(l)) => l.buf().len(),
Rope(Node::Branch(b)) => b.len,
}
}
@@ -67,12 +82,12 @@ impl Rope {
lines: 0,
}));
- fn leaf(buf: &[u8]) -> Rope {
+ fn leaf(buf: &str) -> Rope {
if buf.len() > MAX_NODE_SIZE {
panic!("buffer too long");
}
let mut lines = 0;
- for &b in buf.iter() {
+ for &b in buf.as_bytes().iter() {
if b == b'\n' {
lines += 1;
}
@@ -108,19 +123,20 @@ impl Rope {
fn concat_height(&self, self_height: usize, other: &Rope, other_height: usize) -> Insert {
if let (Rope(Node::Leaf(l)), Rope(Node::Leaf(r))) = (self, other) {
if self.len() + other.len() <= MAX_NODE_SIZE {
- let mut buf = Vec::with_capacity(self.len() + other.len());
- buf.extend_from_slice(l.bytes());
- buf.extend_from_slice(r.bytes());
+ let mut buf = String::with_capacity(self.len() + other.len());
+ buf.push_str(l.buf());
+ buf.push_str(r.buf());
return Insert::Node(Rope::leaf(&buf));
}
if self.len() < MAX_NODE_SIZE / 2 || other.len() < MAX_NODE_SIZE / 2 {
- let mut buf = Vec::with_capacity(self.len() + other.len());
- buf.extend_from_slice(l.bytes());
- buf.extend_from_slice(r.bytes());
- return Insert::Split(
- Rope::leaf(&buf[..buf.len() / 2]),
- Rope::leaf(&buf[buf.len() / 2..]),
- );
+ let mut buf = String::with_capacity(self.len() + other.len());
+ buf.push_str(l.buf());
+ buf.push_str(r.buf());
+ let mut half = buf.len() / 2;
+ while !buf.is_char_boundary(half) {
+ half -= 1;
+ }
+ return Insert::Split(Rope::leaf(&buf[..half]), Rope::leaf(&buf[half..]));
}
return Insert::Split(self.clone(), other.clone());
}
@@ -203,43 +219,52 @@ impl Rope {
}
}
- pub fn read(r: &mut dyn Read) -> Result<Rope, std::io::Error> {
+ pub fn read(r: &mut dyn Read) -> Result<Rope, Box<dyn Error>> {
let mut buf = vec![0; MAX_NODE_SIZE];
let mut rope = Rope::EMPTY;
+ let mut offset = 0;
loop {
- let n = match r.read(&mut buf) {
- Ok(n) => n,
+ let mut n = read(r, &mut buf[offset..])?;
+ n += offset;
+ if n == 0 {
+ break;
+ }
+ let s = match std::str::from_utf8(&buf[..n]) {
+ Ok(s) => s,
Err(err) => {
- if err.kind() == ErrorKind::Interrupted {
- continue;
+ let v = err.valid_up_to();
+ if v == 0 {
+ return Err(Box::new(err));
}
- return Err(err);
+ rope = rope.concat(&Rope::leaf(
+ std::str::from_utf8(&buf[..v]).expect("buf should be valid up to v"),
+ ));
+ offset = n - v;
+ for i in 0..offset {
+ buf[i] = buf[v + i];
+ }
+ continue;
}
};
- if n == 0 {
- break;
- }
- rope = rope.concat(&Rope::leaf(&buf[..n]));
+ rope = rope.concat(&Rope::leaf(s));
+ offset = 0;
}
Ok(rope)
}
- pub fn new(buf: &[u8]) -> Rope {
+ pub fn new(buf: &str) -> Rope {
if buf.is_empty() {
return Rope::EMPTY;
}
if buf.len() <= MAX_NODE_SIZE {
return Rope::leaf(buf);
}
- let mut buf = buf;
- Rope::read(&mut buf).expect("reading from a slice should never fail")
+ Rope::read(&mut buf.as_bytes()).expect("reading from a slice should never fail")
}
fn is_char_boundary(&self, i: usize) -> bool {
match self {
- Rope(Node::Leaf(l)) => {
- return i < l.bytes().len() && l.bytes()[i] & 0xc0 != 0x80;
- }
+ Rope(Node::Leaf(l)) => l.buf().is_char_boundary(i),
Rope(Node::Branch(b)) => {
let mut i = i;
for c in b.children.iter() {
@@ -248,7 +273,7 @@ impl Rope {
}
i -= c.len();
}
- false
+ i == 0
}
}
}
@@ -275,7 +300,7 @@ impl Rope {
match self {
Rope(Node::Leaf(l)) => {
let mut count = 0;
- for (i, &c) in l.bytes().iter().enumerate() {
+ for (i, &c) in l.buf().as_bytes().iter().enumerate() {
if c != b'\n' {
continue;
}
@@ -317,7 +342,7 @@ impl Rope {
pub fn print(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
match self {
Rope(Node::Leaf(l)) => {
- w.write_all(l.bytes())?;
+ w.write_all(l.buf().as_bytes())?;
Ok(())
}
Rope(Node::Branch(b)) => {
@@ -349,7 +374,7 @@ impl Rope {
match self {
Rope(Node::Leaf(l)) => {
let mut lines = 0;
- for &b in l.bytes()[start..end].iter() {
+ for &b in l.buf()[start..end].as_bytes().iter() {
if b == b'\n' {
lines += 1;
}
@@ -388,7 +413,7 @@ impl Rope {
panic!("Index {} out of range 0..{}", i, self.len());
}
match self {
- Rope(Node::Leaf(l)) => l.bytes()[i],
+ Rope(Node::Leaf(l)) => l.buf().as_bytes()[i],
Rope(Node::Branch(b)) => {
let mut i = i;
for c in b.children.iter() {
diff --git a/src/term.rs b/src/term.rs
index e390125..831cd43 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -1,6 +1,6 @@
use libc::{termios, winsize};
use std::error::Error;
-use std::io::{stdout, BufRead, BufReader, Stdin, Write};
+use std::io::{stdout, BufRead, BufReader, Read, Stdin, Write};
const ESC: u8 = 27;
@@ -83,7 +83,7 @@ pub enum Key {
End,
PgDn,
PgUp,
- Byte(u8),
+ Char(char),
}
pub struct Reader {
@@ -110,9 +110,21 @@ impl Reader {
return Ok(Key::Timeout);
}
if buf[0] != ESC {
- let b = buf[0];
- self.stdin.consume(1);
- return Ok(Key::Byte(b));
+ let mut n = usize::try_from(buf[0].leading_ones())
+ .expect("buf[0] is a u8 so it can only have 8 leading ones");
+ if n == 0 {
+ n = 1;
+ }
+ let mut char = [0; 4];
+ self.stdin.read_exact(&mut char[..n])?;
+ let Ok(s) = std::str::from_utf8(&char) else {
+ continue;
+ };
+ return Ok(Key::Char(
+ s.chars()
+ .nth(0)
+ .expect("s should be exactly one codepoint long"),
+ ));
}
if buf.len() < 3 || buf[1] != b'[' {
// Unknown escape sequence, eat the escape and one more byte.
@@ -173,7 +185,7 @@ impl Reader {
}
"3~" => {
// Backspace
- return Ok(Key::Byte(8));
+ return Ok(Key::Char(char::from(b'H' - b'@')));
}
_ => {
continue;