From 20932a88a5dc4d5d25b33074bc5ec45d06d02413 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Mon, 15 Jul 2024 20:54:31 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E5=AE=8C=E5=96=84Command=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=A1=8C=E9=A6=96=E8=A1=8C=E5=B0=BE=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 43 ++++++++++++++++- src/utils/ui/mode/mode.rs | 99 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 45ddf9a..2939864 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -213,8 +213,13 @@ impl EditBuffer { #[inline] pub fn insert_char(&self, ch: u8, x: u16, y: u16) { let mut buf = self.buf.write().unwrap(); - let line = buf.get_mut(self.offset() + y as usize).unwrap(); - line.insert(x as usize, ch); + if buf.len() > 0 { + let line = buf.get_mut(self.offset() + y as usize).unwrap(); + line.insert(x as usize, ch); + } else { + buf.push(LineBuffer::new(vec![ch])); + } + } #[inline] @@ -353,6 +358,40 @@ impl EditBuffer { count } + + pub fn delete_line(&self, y: usize) { + let mut buffer = self.buf.write().unwrap(); + let line = buffer.get(y).unwrap(); + if line.data.is_empty() { + return; + } + + if !line.flags.contains(LineState::LOCKED) { + buffer.remove(y); + } + } + + pub fn delete_until_line_beg(&self, x: usize, y: usize) -> Option{ + let mut buffer = self.buf.write().unwrap(); + let line = buffer.get_mut(y).unwrap(); + + if line.data.len() < 2 { + return None; + } + line.data.drain(0..x); + return Some(x - 1); + } + + pub fn delete_until_endl(&self, x: usize, y: usize) -> Option{ + let mut buffer = self.buf.write().unwrap(); + let line = buffer.get_mut(y).unwrap(); + if line.data.len() < 2 { + return None; + } + line.data.drain(x..); + line.data.push(b'\n'); + return Some(x); + } } bitflags! { diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index c9cdd2f..d3843f7 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -1,9 +1,10 @@ +use std::io::Read; use std::sync::atomic::Ordering; use std::sync::{Mutex, MutexGuard}; use std::{fmt::Debug, io}; use crate::config::lastline_cmd::LastLineCommand; -use crate::utils::buffer::LineState; +use crate::utils::buffer::{self, LineState}; #[cfg(feature = "dragonos")] use crate::utils::input::KeyEventType; @@ -183,6 +184,69 @@ impl Command { Ok(()) } + + fn jump_to_first_char(&self, ui: &mut MutexGuard) -> io::Result { + // 移动到行第一个单词的首字母 + let first_char = { + let line = ui.buffer.get_line(ui.cursor.y()).data; + let mut idx = 0; + for char in line { + if char == b" "[0] { + idx += 1; + } else if char == b"\t"[0] { + idx += 4; + } + } + idx + }; + ui.cursor.move_to_columu(first_char)?; + return Ok(WarpUiCallBackType::None); + } + + fn do_delete_on_d_clicked( + &self, + ui: &mut MutexGuard, + ) -> io::Result { + let buf: &mut [u8] = &mut [0; 8]; + let _ = io::stdin().read(buf)?; + + match buf[0] { + b'd' => { + TermManager::clear_under_cursor()?; + let y = ui.cursor.y() as usize; + ui.buffer.delete_line(y); + let count = ui.buffer.line_count() - y as usize - 1; + ui.render_content(y as u16, count)?; + } + b'0' => { + let x = ui.cursor.x() as usize; + let y = ui.cursor.y() as usize; + match ui.buffer.delete_until_line_beg(x, y) { + Some(..) => { + // 文本变动重新渲染 + ui.cursor.move_to_columu(0)?; + ui.render_content(y as u16, 1)?; + } + None => {} + }; + } + b'$' => { + let x = ui.cursor.x() as usize; + let y = ui.cursor.y() as usize; + match ui.buffer.delete_until_endl(x, y) { + Some(..) => { + ui.cursor.move_left(1)?; + ui.render_content(y as u16, 1)?; + } + None => {} + } + } + _ => {} + } + + Ok(WarpUiCallBackType::None) + + } } impl KeyEventCallback for Command { @@ -218,7 +282,19 @@ impl KeyEventCallback for Command { return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert)); } - b"l" | b"L" => { + // hjkl 与 Vim 的效果一致 + b"h" => self.left(ui), + + // 向下 + b"j" => self.down(ui), + + // 向上 + b"k" => self.up(ui), + + // 向右 + b"l" => self.right(ui), + + b"L" => { // 设置当前行lock let flag = ui.buffer.line_flags(ui.cursor.y()); let offset = ui.buffer.offset(); @@ -257,7 +333,7 @@ impl KeyEventCallback for Command { return Ok(WarpUiCallBackType::None); } - b"w" | b"W" => { + b"W" => { // 跳转到下一个flag行 self.jump_to_next_flag(ui, LineState::FLAGED)?; return Ok(WarpUiCallBackType::None); @@ -273,6 +349,23 @@ impl KeyEventCallback for Command { return Ok(WarpUiCallBackType::None); } + b"0" => { + // 移动到行首 + ui.cursor.move_to_columu(0)?; + return Ok(WarpUiCallBackType::None); + } + + b"^" => self.jump_to_first_char(ui), + + b"$" => { + // 移动到行末 + let line_end = ui.buffer.get_linesize(ui.cursor.y()) - 1; + ui.cursor.move_to_columu(line_end)?; + return Ok(WarpUiCallBackType::None); + } + + b"d" => self.do_delete_on_d_clicked(ui), + _ => { return Ok(WarpUiCallBackType::None); } From 8e83ac6ef7c78e7b00236497383426fc3ac21bce Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Wed, 17 Jul 2024 13:19:41 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86'w'=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E8=87=B3=E4=B8=8B=E4=B8=80=E4=B8=AA=E5=8D=95=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 21 +++++++++--------- src/utils/cursor.rs | 3 ++- src/utils/ui/mode/mode.rs | 46 ++++++++++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 2939864..c0529a3 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -155,7 +155,7 @@ impl EditBuffer { line.data.len() as u16 } - + /// 外部接口,本结构体内部方法不应该使用,因为涉及offset计算 pub fn remove_char(&self, x: u16, y: u16) { let mut buf = self.buf.write().unwrap(); @@ -219,7 +219,6 @@ impl EditBuffer { } else { buf.push(LineBuffer::new(vec![ch])); } - } #[inline] @@ -358,7 +357,7 @@ impl EditBuffer { count } - + pub fn delete_line(&self, y: usize) { let mut buffer = self.buf.write().unwrap(); let line = buffer.get(y).unwrap(); @@ -370,26 +369,26 @@ impl EditBuffer { buffer.remove(y); } } - - pub fn delete_until_line_beg(&self, x: usize, y: usize) -> Option{ + + pub fn delete_until_line_beg(&self, x: usize, y: usize) -> Option { let mut buffer = self.buf.write().unwrap(); let line = buffer.get_mut(y).unwrap(); - + if line.data.len() < 2 { return None; } line.data.drain(0..x); return Some(x - 1); } - - pub fn delete_until_endl(&self, x: usize, y: usize) -> Option{ + + pub fn delete_until_endl(&self, x: usize, y: usize) -> Option { let mut buffer = self.buf.write().unwrap(); let line = buffer.get_mut(y).unwrap(); - if line.data.len() < 2 { + let len = line.data.len(); + if len < 2 { return None; } - line.data.drain(x..); - line.data.push(b'\n'); + line.data.drain(x..len - 1); return Some(x); } } diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs index aaa4d20..b7a0742 100644 --- a/src/utils/cursor.rs +++ b/src/utils/cursor.rs @@ -176,7 +176,8 @@ impl CursorCrtl { let size = *WINSIZE.read().unwrap(); if self.y + lines >= size.rows { // 向上滚动 - todo!() + // todo!() + return Ok(());() } CursorManager::move_to_nextline(lines)?; diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index d3843f7..8edf159 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -4,7 +4,7 @@ use std::sync::{Mutex, MutexGuard}; use std::{fmt::Debug, io}; use crate::config::lastline_cmd::LastLineCommand; -use crate::utils::buffer::{self, LineState}; +use crate::utils::buffer::LineState; #[cfg(feature = "dragonos")] use crate::utils::input::KeyEventType; @@ -208,13 +208,13 @@ impl Command { ui: &mut MutexGuard, ) -> io::Result { let buf: &mut [u8] = &mut [0; 8]; - let _ = io::stdin().read(buf)?; - + let _ = io::stdin().read(buf)?; + match buf[0] { b'd' => { TermManager::clear_under_cursor()?; let y = ui.cursor.y() as usize; - ui.buffer.delete_line(y); + ui.buffer.delete_line(y); let count = ui.buffer.line_count() - y as usize - 1; ui.render_content(y as u16, count)?; } @@ -243,9 +243,37 @@ impl Command { } _ => {} } - - Ok(WarpUiCallBackType::None) - + return Ok(WarpUiCallBackType::None); + } + + fn jump_to_next_word(&self, ui: &mut MutexGuard) -> io::Result { + let x = ui.cursor.x(); + let y = ui.cursor.y(); + let mut left = x as usize; + let mut right = left; + let line = ui.buffer.get_line(y); + let linesize = ui.buffer.get_linesize(y) as usize; + while left <= right && right < linesize { + let l = line[left] as char; + let r = line[right] as char; + if !(l == ' ' || l == '\t' || l == '\n') { + left += 1; + right += 1; + continue; + } + if r == ' ' || l == '\t' || l == '\n' { + right += 1; + } else { + break; + } + } + if right < linesize { + ui.cursor.move_to_columu(right as u16)?; + } else { + self.down(ui)?; + ui.cursor.move_to_columu(0)?; + } + return Ok(WarpUiCallBackType::None); } } @@ -333,6 +361,8 @@ impl KeyEventCallback for Command { return Ok(WarpUiCallBackType::None); } + b"w" => self.jump_to_next_word(ui), + b"W" => { // 跳转到下一个flag行 self.jump_to_next_flag(ui, LineState::FLAGED)?; @@ -363,7 +393,7 @@ impl KeyEventCallback for Command { ui.cursor.move_to_columu(line_end)?; return Ok(WarpUiCallBackType::None); } - + b"d" => self.do_delete_on_d_clicked(ui), _ => { From 4c2efbcbf63bf30c9d867ec36d2004386dcc4320 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sat, 20 Jul 2024 00:12:28 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86'w'=E6=8C=89?= =?UTF-8?q?=E9=94=AE=EF=BC=8C=E5=AE=9E=E7=8E=B0'e'=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E4=B8=8B=E4=B8=80=E5=8D=95=E8=AF=8D=E6=9C=AB=E5=B0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/mode/mode.rs | 85 +++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index 8edf159..663aaaa 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -1,4 +1,6 @@ +use std::borrow::BorrowMut; use std::io::Read; +use std::ops::DerefMut; use std::sync::atomic::Ordering; use std::sync::{Mutex, MutexGuard}; use std::{fmt::Debug, io}; @@ -253,25 +255,82 @@ impl Command { let mut right = left; let line = ui.buffer.get_line(y); let linesize = ui.buffer.get_linesize(y) as usize; + + // 搜索下一个单词的起始位置 while left <= right && right < linesize { - let l = line[left] as char; - let r = line[right] as char; - if !(l == ' ' || l == '\t' || l == '\n') { + let lchar = line[left] as char; + let rchar = line[right] as char; + if !(lchar == ' ' || lchar == '\t') { left += 1; right += 1; continue; } - if r == ' ' || l == '\t' || l == '\n' { - right += 1; - } else { + if rchar != ' ' && rchar != '\t' { break; } + right += 1; } + if right < linesize { + // 如果下一个单词在当前行,则移动光标到该单词的起始位置 ui.cursor.move_to_columu(right as u16)?; - } else { + } else if y + 1 < ui.buffer.line_count() as u16 { + // 如果当前行不是最后一行,则移动到下一行的开头 self.down(ui)?; ui.cursor.move_to_columu(0)?; + } else { + // 如果当前行是最后一行,则移动到当前行的末尾 + ui.cursor.move_to_columu(linesize as u16 - 1)?; + } + return Ok(WarpUiCallBackType::None); + } + + fn jump_to_nextw_ending(&self, ui: &mut MutexGuard) -> io::Result { + let x = ui.cursor.x(); + let y = ui.cursor.y(); + let mut left = x as usize; + let mut right = left; + let line = ui.buffer.get_line(y); + let linesize = ui.buffer.get_linesize(y) as usize; + + // 如果光标已经在当前行的末尾,则尝试移动到下一行的末尾或单词末尾 + if x as usize == linesize - 1 { + if y < ui.buffer.line_count() as u16 - 1 { + self.down(ui)?; + ui.cursor.move_to_columu(0)?; + self.jump_to_nextw_ending(ui)?; + return Ok(WarpUiCallBackType::None); + } else { + // 如果已经是最后一行,则保持光标在当前行的末尾 + ui.cursor.move_to_columu(linesize as u16 - 1)?; + return Ok(WarpUiCallBackType::None); + } + } + + // 搜索下一个单词的末尾 + while left <= right && right < linesize { + let lchar = line[left] as char; + let rchar = line[right] as char; + if lchar == ' ' || lchar == '\t' { + left += 1; + right += 1; + continue; + } + if rchar == ' ' || rchar == '\t' { + if right == x as usize + 1 { + left = right; + continue; + } + right -= 1; + break; + } + right += 1; + } + + if right < linesize { + ui.cursor.move_to_columu(right as u16)?; + } else if right == linesize { + ui.cursor.move_to_columu(linesize as u16 - 1)?; } return Ok(WarpUiCallBackType::None); } @@ -362,6 +421,8 @@ impl KeyEventCallback for Command { } b"w" => self.jump_to_next_word(ui), + + b"e" => self.jump_to_nextw_ending(ui), b"W" => { // 跳转到下一个flag行 @@ -396,6 +457,16 @@ impl KeyEventCallback for Command { b"d" => self.do_delete_on_d_clicked(ui), + b"x" => { + let y = ui.cursor.y(); + let x = ui.cursor.x(); + if x < ui.buffer.get_linesize(y) - 1 { + ui.buffer.remove_char(x, y); + ui.render_content(y, 1)?; + } + return Ok(WarpUiCallBackType::None); + } + _ => { return Ok(WarpUiCallBackType::None); } From 2334e7151d2c957d6df1c3316685809bc6c38eee Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sat, 20 Jul 2024 00:13:50 +0800 Subject: [PATCH 04/10] remove unused imports --- src/utils/ui/mode/mode.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index 663aaaa..d7a6ba7 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -1,6 +1,4 @@ -use std::borrow::BorrowMut; use std::io::Read; -use std::ops::DerefMut; use std::sync::atomic::Ordering; use std::sync::{Mutex, MutexGuard}; use std::{fmt::Debug, io}; From 6ded6c3ea3af2bcbb72a49ffba49834339347160 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sun, 21 Jul 2024 14:42:17 +0800 Subject: [PATCH 05/10] =?UTF-8?q?Refactor=20cursor=20movement=20logic=20fo?= =?UTF-8?q?r=20better=20performance;=20dw=E5=88=A0=E9=99=A4=E5=8D=95?= =?UTF-8?q?=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 97 +++++++++++++++++++++++++ src/utils/cursor.rs | 2 +- src/utils/ui/mode/mode.rs | 145 ++++++++++++++++++++++++-------------- src/utils/ui/uicore.rs | 2 +- 4 files changed, 190 insertions(+), 56 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index c0529a3..af3e77c 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -167,6 +167,16 @@ impl EditBuffer { line.unwrap().remove(x as usize); } + pub fn remove_str(&self, x: u16, y: u16, n: usize) { + let mut buf = self.buf.write().unwrap(); + let line = buf.get_mut(self.offset.load(Ordering::SeqCst) + y as usize); + if line.is_none() { + return; + } + let x = x as usize; + line.unwrap().data.drain(x..x + n); + } + /// 获取一份对应行的拷贝 pub fn get_line(&self, line: u16) -> LineBuffer { let buf = self.buf.read().unwrap(); @@ -391,6 +401,93 @@ impl EditBuffer { line.data.drain(x..len - 1); return Some(x); } + + /// 返回下一个单词的起始位置 + /// 如果为该行最后一单词,返回该行长度 + pub fn search_nextw_beg(&self, x: u16, y: u16) -> usize { + let mut left = x as usize; + let mut right = left; + let linesize = self.get_linesize(y) as usize; + let buf = self.buf.read().unwrap(); + let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); + + while left <= right && right < linesize { + let lchar = line[left] as char; + let rchar = line[right] as char; + if !(lchar == ' ' || lchar == '\t') { + left += 1; + right += 1; + continue; + } + if rchar != ' ' && rchar != '\t' { + break; + } + right += 1; + } + + return right; + } + + /// 搜索下一个单词的末尾 + /// 如果为该行最后一单词,返回该行长度 + pub fn search_nextw_end(&self, x: u16, y: u16) -> usize { + let mut left = x as usize; + let mut right = left; + let linesize = self.get_linesize(y) as usize; + let buf = self.buf.read().unwrap(); + let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); + + while left <= right && right < linesize { + let lchar = line[left] as char; + let rchar = line[right] as char; + if lchar == ' ' || lchar == '\t' { + left += 1; + right += 1; + continue; + } + if rchar == ' ' || rchar == '\t' { + if right == x as usize + 1 { + left = right; + continue; + } + right -= 1; + break; + } + right += 1; + } + + return right; + } + + /// 返回前一单词首字母位置,如果是当前行首单词,返回 None + pub fn search_prevw_beg(&self, x: u16, y: u16) -> Option { + let mut left = x as i32; + let mut right = left; + let buf = self.buf.read().unwrap(); + let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); + + while left <= right && left >= 0 { + let lchar = line[left as usize] as char; + let rchar = line[right as usize] as char; + + if rchar == ' ' || rchar == '\t' { + left -= 1; + right -= 1; + continue; + } + + if lchar == ' ' || lchar == '\t' { + if left + 1 == x.into() { + right = left; + continue; + } + return Some(left as usize + 1); + } + + left -= 1; + } + return None; + } } bitflags! { diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs index b7a0742..1788695 100644 --- a/src/utils/cursor.rs +++ b/src/utils/cursor.rs @@ -177,7 +177,7 @@ impl CursorCrtl { if self.y + lines >= size.rows { // 向上滚动 // todo!() - return Ok(());() + return Ok(()); } CursorManager::move_to_nextline(lines)?; diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index d7a6ba7..6e29c52 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -241,6 +241,44 @@ impl Command { None => {} } } + + b'w' | b'e' => { + let x = ui.cursor.x(); + let y = ui.cursor.y(); + let next_word_pos = ui.buffer.search_nextw_beg(x, y); + let linesize = ui.buffer.get_linesize(y); + + // 如果下一个单词在当前行,则删除当前单词 + if next_word_pos < linesize.into() { + ui.buffer.remove_str(x, y, next_word_pos - x as usize); + } else { + // 如果下一个单词在下一行,则删除当前行剩余部分 + self.left(ui)?; + ui.buffer.delete_until_endl(x.into(), y.into()); + } + ui.render_content(y, 1)?; + } + + b'b' => { + let old_x = ui.cursor.x(); + let old_y = ui.cursor.y(); + + self.jump_to_prevw_beg(ui)?; + + let x = ui.cursor.x(); + let y = ui.cursor.y(); + if old_y == y { + ui.buffer.remove_str(x, y, old_x as usize - x as usize); + ui.render_content(y, 1)?; + } else { + ui.buffer.delete_until_endl(x as usize, y as usize); + ui.buffer.delete_until_line_beg(old_x as usize, old_y as usize); + ui.buffer.merge_line(old_y); + let linecount = ui.buffer.line_count(); + TermManager::clear_under_cursor()?; + ui.render_content(y, linecount - y as usize - 1)?; + } + } _ => {} } return Ok(WarpUiCallBackType::None); @@ -249,29 +287,12 @@ impl Command { fn jump_to_next_word(&self, ui: &mut MutexGuard) -> io::Result { let x = ui.cursor.x(); let y = ui.cursor.y(); - let mut left = x as usize; - let mut right = left; - let line = ui.buffer.get_line(y); - let linesize = ui.buffer.get_linesize(y) as usize; - - // 搜索下一个单词的起始位置 - while left <= right && right < linesize { - let lchar = line[left] as char; - let rchar = line[right] as char; - if !(lchar == ' ' || lchar == '\t') { - left += 1; - right += 1; - continue; - } - if rchar != ' ' && rchar != '\t' { - break; - } - right += 1; - } + let pos = ui.buffer.search_nextw_beg(x, y); + let linesize = ui.buffer.get_linesize(y); - if right < linesize { + if pos < linesize as usize { // 如果下一个单词在当前行,则移动光标到该单词的起始位置 - ui.cursor.move_to_columu(right as u16)?; + ui.cursor.move_to_columu(pos as u16)?; } else if y + 1 < ui.buffer.line_count() as u16 { // 如果当前行不是最后一行,则移动到下一行的开头 self.down(ui)?; @@ -286,51 +307,55 @@ impl Command { fn jump_to_nextw_ending(&self, ui: &mut MutexGuard) -> io::Result { let x = ui.cursor.x(); let y = ui.cursor.y(); - let mut left = x as usize; - let mut right = left; - let line = ui.buffer.get_line(y); let linesize = ui.buffer.get_linesize(y) as usize; // 如果光标已经在当前行的末尾,则尝试移动到下一行的末尾或单词末尾 - if x as usize == linesize - 1 { + if x as usize >= linesize - 2 { if y < ui.buffer.line_count() as u16 - 1 { - self.down(ui)?; - ui.cursor.move_to_columu(0)?; - self.jump_to_nextw_ending(ui)?; - return Ok(WarpUiCallBackType::None); + let next_end_pos = ui.buffer.search_nextw_end(0, y + 1) as u16; + ui.cursor.move_to(next_end_pos, y + 1)?; + ui.cursor.highlight(Some(y))?; } else { // 如果已经是最后一行,则保持光标在当前行的末尾 ui.cursor.move_to_columu(linesize as u16 - 1)?; - return Ok(WarpUiCallBackType::None); } + return Ok(WarpUiCallBackType::None); } - // 搜索下一个单词的末尾 - while left <= right && right < linesize { - let lchar = line[left] as char; - let rchar = line[right] as char; - if lchar == ' ' || lchar == '\t' { - left += 1; - right += 1; - continue; - } - if rchar == ' ' || rchar == '\t' { - if right == x as usize + 1 { - left = right; - continue; - } - right -= 1; - break; - } - right += 1; - } + let next_end_pos = ui.buffer.search_nextw_end(x, y) as u16; + // 如果下一个单词的末尾在当前行,则移动光标到该单词的末尾 + ui.cursor.move_to_columu(next_end_pos.min(linesize as u16 - 2))?; + return Ok(WarpUiCallBackType::None); + } - if right < linesize { - ui.cursor.move_to_columu(right as u16)?; - } else if right == linesize { - ui.cursor.move_to_columu(linesize as u16 - 1)?; + fn jump_to_prevw_beg(&self, ui: &mut MutexGuard) -> io::Result { + let x = ui.cursor.x(); + let y = ui.cursor.y(); + + // 如果光标已在行首,则尝试移动到上一行的单词首字母 + if x == 0 { + if y > 0 { + let end_of_prev_line = ui.buffer.get_linesize(y - 1) - 1; + let prev_word_pos = match ui.buffer.search_prevw_beg(end_of_prev_line, y - 1) { + Some(pos) => pos, + None => 0 + }; + ui.cursor.move_to(prev_word_pos as u16, y - 1)?; + ui.cursor.highlight(Some(y))?; + } else { + // 如果已经是第一行,则保持光标在当前行的起始位置 + ui.cursor.move_to_columu(0)?; + } + return Ok(WarpUiCallBackType::None); } - return Ok(WarpUiCallBackType::None); + + let prev_word_pos = match ui.buffer.search_prevw_beg(x, y) { + Some(pos) => pos, + None => 0 + }; + + ui.cursor.move_to(prev_word_pos as u16, y)?; + return Ok(WarpUiCallBackType::None); } } @@ -421,6 +446,8 @@ impl KeyEventCallback for Command { b"w" => self.jump_to_next_word(ui), b"e" => self.jump_to_nextw_ending(ui), + + b"b" => self.jump_to_prevw_beg(ui), b"W" => { // 跳转到下一个flag行 @@ -464,6 +491,16 @@ impl KeyEventCallback for Command { } return Ok(WarpUiCallBackType::None); } + + b"G" => { + // 移动到最后一行 + let line_count = ui.buffer.line_count() as u16; + let y = ui.cursor.y(); + ui.scroll_down(line_count - y)?; + ui.cursor.move_to_row(line_count - 1)?; + ui.cursor.highlight(Some(y))?; + return Ok(WarpUiCallBackType::None); + } _ => { return Ok(WarpUiCallBackType::None); diff --git a/src/utils/ui/uicore.rs b/src/utils/ui/uicore.rs index e805438..38206bf 100644 --- a/src/utils/ui/uicore.rs +++ b/src/utils/ui/uicore.rs @@ -213,11 +213,11 @@ impl UiCore { } self.buffer.set_offset(offset - count as usize); - // 将光标移动第count行 // 执行滚动 TermManager::scroll_down(count)?; + // 将光标移动第count行 self.cursor.move_to_row(count - 1)?; // 清除光标以上的内容 TermManager::clear_up_cursor()?; From e51971fd6cb50a6178f3c5d7457683765f3a2ab6 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sun, 21 Jul 2024 16:01:16 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 4 ++-- src/utils/ui/mode/mode.rs | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index af3e77c..9004d10 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -404,7 +404,7 @@ impl EditBuffer { /// 返回下一个单词的起始位置 /// 如果为该行最后一单词,返回该行长度 - pub fn search_nextw_beg(&self, x: u16, y: u16) -> usize { + pub fn search_nextw_begin(&self, x: u16, y: u16) -> usize { let mut left = x as usize; let mut right = left; let linesize = self.get_linesize(y) as usize; @@ -460,7 +460,7 @@ impl EditBuffer { } /// 返回前一单词首字母位置,如果是当前行首单词,返回 None - pub fn search_prevw_beg(&self, x: u16, y: u16) -> Option { + pub fn search_prevw_begin(&self, x: u16, y: u16) -> Option { let mut left = x as i32; let mut right = left; let buf = self.buf.read().unwrap(); diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index 6e29c52..58e6adb 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -245,7 +245,7 @@ impl Command { b'w' | b'e' => { let x = ui.cursor.x(); let y = ui.cursor.y(); - let next_word_pos = ui.buffer.search_nextw_beg(x, y); + let next_word_pos = ui.buffer.search_nextw_begin(x, y); let linesize = ui.buffer.get_linesize(y); // 如果下一个单词在当前行,则删除当前单词 @@ -287,7 +287,7 @@ impl Command { fn jump_to_next_word(&self, ui: &mut MutexGuard) -> io::Result { let x = ui.cursor.x(); let y = ui.cursor.y(); - let pos = ui.buffer.search_nextw_beg(x, y); + let pos = ui.buffer.search_nextw_begin(x, y); let linesize = ui.buffer.get_linesize(y); if pos < linesize as usize { @@ -309,8 +309,9 @@ impl Command { let y = ui.cursor.y(); let linesize = ui.buffer.get_linesize(y) as usize; - // 如果光标已经在当前行的末尾,则尝试移动到下一行的末尾或单词末尾 - if x as usize >= linesize - 2 { + // 如果光标已经在当前行的末尾或最后一个字符,则尝试移动到下一行的末尾或单词末尾 + let final_char_pos = linesize - 2; + if x as usize >= final_char_pos { if y < ui.buffer.line_count() as u16 - 1 { let next_end_pos = ui.buffer.search_nextw_end(0, y + 1) as u16; ui.cursor.move_to(next_end_pos, y + 1)?; @@ -336,7 +337,7 @@ impl Command { if x == 0 { if y > 0 { let end_of_prev_line = ui.buffer.get_linesize(y - 1) - 1; - let prev_word_pos = match ui.buffer.search_prevw_beg(end_of_prev_line, y - 1) { + let prev_word_pos = match ui.buffer.search_prevw_begin(end_of_prev_line, y - 1) { Some(pos) => pos, None => 0 }; @@ -349,7 +350,7 @@ impl Command { return Ok(WarpUiCallBackType::None); } - let prev_word_pos = match ui.buffer.search_prevw_beg(x, y) { + let prev_word_pos = match ui.buffer.search_prevw_begin(x, y) { Some(pos) => pos, None => 0 }; From 97f99d7d074bf33963ab2afdee6d77c3fc5489d0 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sun, 21 Jul 2024 16:05:03 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 34 +++++++++++++++------------ src/utils/ui/mode/mode.rs | 48 ++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 9004d10..0ce422e 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -155,7 +155,7 @@ impl EditBuffer { line.data.len() as u16 } - + /// 外部接口,本结构体内部方法不应该使用,因为涉及offset计算 pub fn remove_char(&self, x: u16, y: u16) { let mut buf = self.buf.write().unwrap(); @@ -401,7 +401,7 @@ impl EditBuffer { line.data.drain(x..len - 1); return Some(x); } - + /// 返回下一个单词的起始位置 /// 如果为该行最后一单词,返回该行长度 pub fn search_nextw_begin(&self, x: u16, y: u16) -> usize { @@ -409,8 +409,10 @@ impl EditBuffer { let mut right = left; let linesize = self.get_linesize(y) as usize; let buf = self.buf.read().unwrap(); - let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); - + let line = buf + .get(self.offset.load(Ordering::SeqCst) + y as usize) + .unwrap(); + while left <= right && right < linesize { let lchar = line[left] as char; let rchar = line[right] as char; @@ -424,10 +426,10 @@ impl EditBuffer { } right += 1; } - + return right; } - + /// 搜索下一个单词的末尾 /// 如果为该行最后一单词,返回该行长度 pub fn search_nextw_end(&self, x: u16, y: u16) -> usize { @@ -435,8 +437,10 @@ impl EditBuffer { let mut right = left; let linesize = self.get_linesize(y) as usize; let buf = self.buf.read().unwrap(); - let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); - + let line = buf + .get(self.offset.load(Ordering::SeqCst) + y as usize) + .unwrap(); + while left <= right && right < linesize { let lchar = line[left] as char; let rchar = line[right] as char; @@ -458,24 +462,26 @@ impl EditBuffer { return right; } - + /// 返回前一单词首字母位置,如果是当前行首单词,返回 None pub fn search_prevw_begin(&self, x: u16, y: u16) -> Option { let mut left = x as i32; let mut right = left; let buf = self.buf.read().unwrap(); - let line = buf.get(self.offset.load(Ordering::SeqCst) + y as usize).unwrap(); - + let line = buf + .get(self.offset.load(Ordering::SeqCst) + y as usize) + .unwrap(); + while left <= right && left >= 0 { let lchar = line[left as usize] as char; let rchar = line[right as usize] as char; - + if rchar == ' ' || rchar == '\t' { left -= 1; right -= 1; continue; } - + if lchar == ' ' || lchar == '\t' { if left + 1 == x.into() { right = left; @@ -483,7 +489,7 @@ impl EditBuffer { } return Some(left as usize + 1); } - + left -= 1; } return None; diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index 58e6adb..58d1e1a 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -241,13 +241,13 @@ impl Command { None => {} } } - + b'w' | b'e' => { let x = ui.cursor.x(); let y = ui.cursor.y(); let next_word_pos = ui.buffer.search_nextw_begin(x, y); let linesize = ui.buffer.get_linesize(y); - + // 如果下一个单词在当前行,则删除当前单词 if next_word_pos < linesize.into() { ui.buffer.remove_str(x, y, next_word_pos - x as usize); @@ -255,14 +255,14 @@ impl Command { // 如果下一个单词在下一行,则删除当前行剩余部分 self.left(ui)?; ui.buffer.delete_until_endl(x.into(), y.into()); - } + } ui.render_content(y, 1)?; } - + b'b' => { let old_x = ui.cursor.x(); let old_y = ui.cursor.y(); - + self.jump_to_prevw_beg(ui)?; let x = ui.cursor.x(); @@ -272,7 +272,8 @@ impl Command { ui.render_content(y, 1)?; } else { ui.buffer.delete_until_endl(x as usize, y as usize); - ui.buffer.delete_until_line_beg(old_x as usize, old_y as usize); + ui.buffer + .delete_until_line_beg(old_x as usize, old_y as usize); ui.buffer.merge_line(old_y); let linecount = ui.buffer.line_count(); TermManager::clear_under_cursor()?; @@ -291,24 +292,24 @@ impl Command { let linesize = ui.buffer.get_linesize(y); if pos < linesize as usize { - // 如果下一个单词在当前行,则移动光标到该单词的起始位置 + // 如果下一个单词在当前行,则移动光标到该单词的起始位置 ui.cursor.move_to_columu(pos as u16)?; } else if y + 1 < ui.buffer.line_count() as u16 { // 如果当前行不是最后一行,则移动到下一行的开头 self.down(ui)?; ui.cursor.move_to_columu(0)?; } else { - // 如果当前行是最后一行,则移动到当前行的末尾 + // 如果当前行是最后一行,则移动到当前行的末尾 ui.cursor.move_to_columu(linesize as u16 - 1)?; } return Ok(WarpUiCallBackType::None); } - + fn jump_to_nextw_ending(&self, ui: &mut MutexGuard) -> io::Result { let x = ui.cursor.x(); let y = ui.cursor.y(); let linesize = ui.buffer.get_linesize(y) as usize; - + // 如果光标已经在当前行的末尾或最后一个字符,则尝试移动到下一行的末尾或单词末尾 let final_char_pos = linesize - 2; if x as usize >= final_char_pos { @@ -317,29 +318,30 @@ impl Command { ui.cursor.move_to(next_end_pos, y + 1)?; ui.cursor.highlight(Some(y))?; } else { - // 如果已经是最后一行,则保持光标在当前行的末尾 - ui.cursor.move_to_columu(linesize as u16 - 1)?; + // 如果已经是最后一行,则保持光标在当前行的末尾 + ui.cursor.move_to_columu(linesize as u16 - 1)?; } return Ok(WarpUiCallBackType::None); } - + let next_end_pos = ui.buffer.search_nextw_end(x, y) as u16; // 如果下一个单词的末尾在当前行,则移动光标到该单词的末尾 - ui.cursor.move_to_columu(next_end_pos.min(linesize as u16 - 2))?; + ui.cursor + .move_to_columu(next_end_pos.min(linesize as u16 - 2))?; return Ok(WarpUiCallBackType::None); } fn jump_to_prevw_beg(&self, ui: &mut MutexGuard) -> io::Result { let x = ui.cursor.x(); let y = ui.cursor.y(); - + // 如果光标已在行首,则尝试移动到上一行的单词首字母 if x == 0 { if y > 0 { let end_of_prev_line = ui.buffer.get_linesize(y - 1) - 1; let prev_word_pos = match ui.buffer.search_prevw_begin(end_of_prev_line, y - 1) { Some(pos) => pos, - None => 0 + None => 0, }; ui.cursor.move_to(prev_word_pos as u16, y - 1)?; ui.cursor.highlight(Some(y))?; @@ -349,14 +351,14 @@ impl Command { } return Ok(WarpUiCallBackType::None); } - + let prev_word_pos = match ui.buffer.search_prevw_begin(x, y) { Some(pos) => pos, - None => 0 + None => 0, }; - + ui.cursor.move_to(prev_word_pos as u16, y)?; - return Ok(WarpUiCallBackType::None); + return Ok(WarpUiCallBackType::None); } } @@ -445,9 +447,9 @@ impl KeyEventCallback for Command { } b"w" => self.jump_to_next_word(ui), - + b"e" => self.jump_to_nextw_ending(ui), - + b"b" => self.jump_to_prevw_beg(ui), b"W" => { @@ -492,7 +494,7 @@ impl KeyEventCallback for Command { } return Ok(WarpUiCallBackType::None); } - + b"G" => { // 移动到最后一行 let line_count = ui.buffer.line_count() as u16; From ff33d29dfc90a3376df3e20b09bc59985536bff5 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Sun, 21 Jul 2024 23:15:15 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E8=A1=8C=E7=9A=84=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 12 +++++++----- src/utils/ui/mode/mode.rs | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 0ce422e..7489e08 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -223,11 +223,13 @@ impl EditBuffer { #[inline] pub fn insert_char(&self, ch: u8, x: u16, y: u16) { let mut buf = self.buf.write().unwrap(); - if buf.len() > 0 { - let line = buf.get_mut(self.offset() + y as usize).unwrap(); - line.insert(x as usize, ch); - } else { - buf.push(LineBuffer::new(vec![ch])); + let line = buf.get_mut(self.offset() + y as usize); + match line { + Some(line) => line.insert(x as usize, ch), + None => { + let newline = vec!['\n' as u8]; + buf.push(LineBuffer::new(newline)); + } } } diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index 58d1e1a..d4c8789 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -214,9 +214,21 @@ impl Command { b'd' => { TermManager::clear_under_cursor()?; let y = ui.cursor.y() as usize; + let old_line_count = ui.buffer.line_count(); + + let count = old_line_count - y as usize; ui.buffer.delete_line(y); - let count = ui.buffer.line_count() - y as usize - 1; - ui.render_content(y as u16, count)?; + ui.render_content(y as u16, count.max(1))?; + + if y == old_line_count - 1 { + self.up(ui)?; + } + + if old_line_count == 1 { + ui.cursor.move_to_columu(0)?; + ui.buffer.insert_char('\n' as u8, 0, 0); + ui.render_content(0, 1)?; + } } b'0' => { let x = ui.cursor.x() as usize; From b20ec2bd99cba00a08e6e23f565efcb5ed4a9a56 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Mon, 22 Jul 2024 00:09:26 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E6=B8=B2=E6=9F=93=E5=BC=82=E5=B8=B8=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/mode/mode.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index d4c8789..a705c58 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -212,6 +212,7 @@ impl Command { match buf[0] { b'd' => { + TermManager::clear_current_line()?; TermManager::clear_under_cursor()?; let y = ui.cursor.y() as usize; let old_line_count = ui.buffer.line_count(); From 6d33fdb194fe2bae2873edcc109f8400d07364a5 Mon Sep 17 00:00:00 2001 From: trv3wood <1841369469@qq.com> Date: Mon, 22 Jul 2024 11:44:48 +0800 Subject: [PATCH 10/10] =?UTF-8?q?G=E7=A7=BB=E5=8A=A8=E5=88=B0=E6=9C=80?= =?UTF-8?q?=E5=90=8E=E4=B8=80=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/mode/mode.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/ui/mode/mode.rs b/src/utils/ui/mode/mode.rs index a705c58..42ecf3f 100644 --- a/src/utils/ui/mode/mode.rs +++ b/src/utils/ui/mode/mode.rs @@ -512,8 +512,9 @@ impl KeyEventCallback for Command { // 移动到最后一行 let line_count = ui.buffer.line_count() as u16; let y = ui.cursor.y(); - ui.scroll_down(line_count - y)?; - ui.cursor.move_to_row(line_count - 1)?; + let new_y = ui.buffer.goto_line(line_count as usize - 1); + ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?; + ui.cursor.move_to_row(new_y)?; ui.cursor.highlight(Some(y))?; return Ok(WarpUiCallBackType::None); }