From 9940e06f7fa7276eb1bee96072a4d93645604fac Mon Sep 17 00:00:00 2001 From: trv3wood <202330452442@mail.scut.edu.cn> Date: Thu, 12 Sep 2024 09:05:26 +0000 Subject: [PATCH 1/5] =?UTF-8?q?=E5=89=AA=E8=B4=B4=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 19 ++++++++++++++ src/utils/mod.rs | 1 + src/utils/reg.rs | 32 ++++++++++++++++++++++++ src/utils/ui/mode/common.rs | 46 +++++++++++++++++++++++++++++++++- src/utils/ui/mode/normal.rs | 50 +++++++++++++++++++++++++++++++++++++ src/utils/ui/uicore.rs | 5 ++-- 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 src/utils/reg.rs diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index b4b220c..44d0f20 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -251,6 +251,25 @@ impl EditBuffer { } } + #[inline] pub fn get_range(&self, start_pos: (u16, u16), end_pos: (u16, u16)) -> Vec { + let buf = self.buf.read().unwrap(); + let mut ret = Vec::new(); + let start = self.offset.load(Ordering::SeqCst) + start_pos.1 as usize; + let end = self.offset.load(Ordering::SeqCst) + end_pos.1 as usize; + // 从起始行到结束行 + for (idx, line) in buf.iter().enumerate().skip(start).take(end - start + 1) { + let mut newline = line.clone(); + if idx == start { + newline.data.drain(0..start_pos.0 as usize); + } + if idx == end { + newline.data.drain(end_pos.0 as usize..); + } + ret.push(newline); + } + ret + } + #[inline] pub fn all_buffer(&self) -> Vec { self.buf.read().unwrap().clone() diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2427952..3499230 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -9,3 +9,4 @@ pub mod style; pub mod term_io; pub mod terminal; pub mod ui; +pub mod reg; \ No newline at end of file diff --git a/src/utils/reg.rs b/src/utils/reg.rs new file mode 100644 index 0000000..0a9d004 --- /dev/null +++ b/src/utils/reg.rs @@ -0,0 +1,32 @@ +use super::buffer::LineBuffer; + +#[derive(Debug)] +pub struct Register { + pub text: Vec +} + +impl Register { + pub fn is_muti_line(&self) -> bool { + return self.text.len() > 1; + } + + pub fn is_single_line(&self) -> bool { + return self.text.len() == 1 && self.text[0].ends_with(b"\n"); + } + + pub fn new() -> Register { + Register { + text: Vec::new() + } + } + + pub fn from(lines: Vec) -> Register { + Register { + text: Vec::from(lines) + } + } + + pub fn copy(&mut self, lines: Vec) { + self.text = Vec::from(lines); + } +} \ No newline at end of file diff --git a/src/utils/ui/mode/common.rs b/src/utils/ui/mode/common.rs index 9f8b6ba..fa43e70 100644 --- a/src/utils/ui/mode/common.rs +++ b/src/utils/ui/mode/common.rs @@ -1,4 +1,4 @@ -use std::{io, sync::MutexGuard}; +use std::{io, sync::MutexGuard, usize}; use crate::utils::{ terminal::TermManager, @@ -134,4 +134,48 @@ pub trait CommonOp: KeyEventCallback { // 如果下一个单词的末尾在当前行,则移动光标到该单词的末尾 return (next_end_pos.min(linesize as u16 - 1), abs_y); } + + fn locate_next_word(&self, ui: &mut MutexGuard, x: u16, y: u16) -> (u16, u16) { + let linesize = ui.buffer.get_linesize(y) as usize; + let abs_y = ui.buffer.offset() as u16 + y; + let next_word_pos = ui.buffer.search_nextw_begin(x, y); + + if next_word_pos < linesize { + return (next_word_pos as u16, abs_y); + } + + if abs_y < ui.buffer.line_count() as u16 - 1 { + let next_word_pos = ui.buffer.search_nextw_begin(0, y + 1) as u16; + return (next_word_pos, abs_y + 1); + } + + return (linesize as u16 - 1, abs_y); + } + + fn paste(&self, ui: &mut MutexGuard) -> io::Result<()> { + let x = ui.cursor.x(); + let y = ui.cursor.y(); + if ui.register.text.is_empty() { + return Ok(()); + } + if ui.register.is_single_line() { // 单行 + ui.buffer.insert_line(y.into(), &ui.register.text[0]); + } else if ui.register.is_muti_line() { // 多行 + for (idx, line) in ui.register.text.iter().enumerate() { + for (idy, c) in line.data.iter().enumerate() { + ui.buffer.insert_char(*c, x + idy as u16, y + idx as u16); + } + ui.buffer + .input_enter(line.data.len() as u16, y + idx as u16); + } + } else { // 单词 + let line = &ui.register.text[0]; + for (idx, c) in line.data.iter().enumerate() { + ui.buffer.insert_char(*c, x + idx as u16, y); + } + } + let rest_line = ui.buffer.line_count() - y as usize - ui.buffer.offset(); + ui.render_content(y, rest_line)?; + Ok(()) + } } diff --git a/src/utils/ui/mode/normal.rs b/src/utils/ui/mode/normal.rs index 4bb712d..f2f4057 100644 --- a/src/utils/ui/mode/normal.rs +++ b/src/utils/ui/mode/normal.rs @@ -156,6 +156,8 @@ impl KeyEventCallback for Normal { b"x" => { normal_state.on_x_clicked(); } + b"y" => normal_state.on_y_clicked(), + b"p" => normal_state.on_p_clicked(), _ => {} } return normal_state.handle(ui); @@ -611,6 +613,52 @@ impl NormalState { } return Ok(WarpUiCallBackType::None); } + + fn on_y_clicked(&mut self) { + match self.cmdchar { + Some('y') => { + self.buf_op_arg = Some(BufOpArg::Line); + } + Some('w') => { + self.buf_op_arg = Some(BufOpArg::Word); + } + None => { + self.cmdchar = Some('y'); + } + _ => {} + } + } + + fn exec_y_cmd(&mut self, ui: &mut MutexGuard) -> io::Result { + match self.buf_op_arg { + Some(BufOpArg::Line) => { + let y = ui.cursor.y(); + let line = ui.buffer.get_line(y); + ui.register.copy(vec![line]); + self.reset(); + } + Some(BufOpArg::Word) => { + let curr_pos = (ui.cursor.x(), ui.cursor.y()); + let next_pos = self.locate_next_word(ui, curr_pos.0, curr_pos.1); + let text_copy = ui.buffer.get_range(curr_pos, next_pos); + ui.register.copy(text_copy); + self.reset(); + } + _ => {} + } + return Ok(WarpUiCallBackType::None); + } + + fn on_p_clicked(&mut self) { + if self.cmdchar.is_none() { + self.cmdchar = Some('p'); + } + } + + fn exec_p_cmd(&mut self, ui: &mut MutexGuard) -> io::Result { + self.reset(); + self.paste(ui).map(|_| WarpUiCallBackType::None) + } } pub trait StateMachine { @@ -640,6 +688,8 @@ impl StateMachine for NormalState { 'f' => self.exec_f_cmd(ui), 'F' => self.exec_F_cmd(ui), 'x' => self.exec_x_cmd(ui), + 'y' => self.exec_y_cmd(ui), + 'p' => self.exec_p_cmd(ui), _ => return Ok(WarpUiCallBackType::None), } } diff --git a/src/utils/ui/uicore.rs b/src/utils/ui/uicore.rs index ccf86ad..c5286b0 100644 --- a/src/utils/ui/uicore.rs +++ b/src/utils/ui/uicore.rs @@ -12,8 +12,7 @@ use lazy_static::lazy_static; use crate::{ config::appconfig::AppSetting, utils::{ - buffer::EditBuffer, cursor::CursorCrtl, style::StyleManager, terminal::TermManager, - ui::InfoLevel, + buffer::EditBuffer, cursor::CursorCrtl, reg::Register, style::StyleManager, terminal::TermManager, ui::InfoLevel }, }; @@ -49,6 +48,7 @@ pub struct WinSize { pub struct UiCore { pub buffer: Arc, pub cursor: CursorCrtl, + pub register: Register, #[allow(dead_code)] setting: AppSetting, @@ -67,6 +67,7 @@ impl UiCore { setting, edited: false, edited_once: Once::new(), + register: Register::new(), } } From 600c1d458e799a6414da3193e0040985c74c5c37 Mon Sep 17 00:00:00 2001 From: trv3wood <202330452442@mail.scut.edu.cn> Date: Thu, 12 Sep 2024 09:07:05 +0000 Subject: [PATCH 2/5] fmt --- src/utils/buffer.rs | 3 ++- src/utils/mod.rs | 2 +- src/utils/reg.rs | 12 +++++------- src/utils/ui/mode/common.rs | 9 ++++++--- src/utils/ui/uicore.rs | 3 ++- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 44d0f20..612432c 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -251,7 +251,8 @@ impl EditBuffer { } } - #[inline] pub fn get_range(&self, start_pos: (u16, u16), end_pos: (u16, u16)) -> Vec { + #[inline] + pub fn get_range(&self, start_pos: (u16, u16), end_pos: (u16, u16)) -> Vec { let buf = self.buf.read().unwrap(); let mut ret = Vec::new(); let start = self.offset.load(Ordering::SeqCst) + start_pos.1 as usize; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 3499230..097e1ce 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -5,8 +5,8 @@ pub mod file; #[cfg(feature = "dragonos")] pub mod input; pub mod log_util; +pub mod reg; pub mod style; pub mod term_io; pub mod terminal; pub mod ui; -pub mod reg; \ No newline at end of file diff --git a/src/utils/reg.rs b/src/utils/reg.rs index 0a9d004..fdd3cb3 100644 --- a/src/utils/reg.rs +++ b/src/utils/reg.rs @@ -2,31 +2,29 @@ use super::buffer::LineBuffer; #[derive(Debug)] pub struct Register { - pub text: Vec + pub text: Vec, } impl Register { pub fn is_muti_line(&self) -> bool { return self.text.len() > 1; } - + pub fn is_single_line(&self) -> bool { return self.text.len() == 1 && self.text[0].ends_with(b"\n"); } pub fn new() -> Register { - Register { - text: Vec::new() - } + Register { text: Vec::new() } } pub fn from(lines: Vec) -> Register { Register { - text: Vec::from(lines) + text: Vec::from(lines), } } pub fn copy(&mut self, lines: Vec) { self.text = Vec::from(lines); } -} \ No newline at end of file +} diff --git a/src/utils/ui/mode/common.rs b/src/utils/ui/mode/common.rs index fa43e70..c9b8796 100644 --- a/src/utils/ui/mode/common.rs +++ b/src/utils/ui/mode/common.rs @@ -158,9 +158,11 @@ pub trait CommonOp: KeyEventCallback { if ui.register.text.is_empty() { return Ok(()); } - if ui.register.is_single_line() { // 单行 + if ui.register.is_single_line() { + // 单行 ui.buffer.insert_line(y.into(), &ui.register.text[0]); - } else if ui.register.is_muti_line() { // 多行 + } else if ui.register.is_muti_line() { + // 多行 for (idx, line) in ui.register.text.iter().enumerate() { for (idy, c) in line.data.iter().enumerate() { ui.buffer.insert_char(*c, x + idy as u16, y + idx as u16); @@ -168,7 +170,8 @@ pub trait CommonOp: KeyEventCallback { ui.buffer .input_enter(line.data.len() as u16, y + idx as u16); } - } else { // 单词 + } else { + // 单词 let line = &ui.register.text[0]; for (idx, c) in line.data.iter().enumerate() { ui.buffer.insert_char(*c, x + idx as u16, y); diff --git a/src/utils/ui/uicore.rs b/src/utils/ui/uicore.rs index c5286b0..1ce4f35 100644 --- a/src/utils/ui/uicore.rs +++ b/src/utils/ui/uicore.rs @@ -12,7 +12,8 @@ use lazy_static::lazy_static; use crate::{ config::appconfig::AppSetting, utils::{ - buffer::EditBuffer, cursor::CursorCrtl, reg::Register, style::StyleManager, terminal::TermManager, ui::InfoLevel + buffer::EditBuffer, cursor::CursorCrtl, reg::Register, style::StyleManager, + terminal::TermManager, ui::InfoLevel, }, }; From e4e41a2e8520b725b47087a4721a81a1558a063c Mon Sep 17 00:00:00 2001 From: trv3wood <202330452442@mail.scut.edu.cn> Date: Thu, 12 Sep 2024 12:29:07 +0000 Subject: [PATCH 3/5] =?UTF-8?q?n=20+=20p=E8=BF=9E=E7=BB=AD=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 4 ++-- src/utils/ui/mode/normal.rs | 39 ++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 612432c..77cccfb 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -372,9 +372,9 @@ impl EditBuffer { if offset + win_rows > max_line { // 最底下无数据,则调整 - let adapt_offset = max_line - win_rows; + let adapt_offset = max_line.saturating_sub(win_rows); - y += offset - adapt_offset; + y = y.saturating_add(offset).saturating_sub(adapt_offset); offset = adapt_offset; } diff --git a/src/utils/ui/mode/normal.rs b/src/utils/ui/mode/normal.rs index f2f4057..e8cc7e4 100644 --- a/src/utils/ui/mode/normal.rs +++ b/src/utils/ui/mode/normal.rs @@ -191,16 +191,6 @@ impl KeyEventCallback for NormalState { } } impl NormalState { - pub fn reset(&mut self) { - self.cmdchar = None; - self.count = None; - self.count0 = false; - self.start_pos = None; - self.end_pos = None; - self.cmdbuf.clear(); - self.buf_op_arg = None; - } - pub fn exec_0_cmd(&mut self, ui: &mut MutexGuard) -> io::Result { ui.cursor.move_to_columu(0)?; self.reset(); @@ -335,8 +325,7 @@ impl NormalState { } } pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard) -> io::Result { - self.exit()?; - return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert)); + return self.exit(WarpUiCallBackType::ChangMode(ModeType::Insert)); } /// 处理输入的非零数字 @@ -656,17 +645,35 @@ impl NormalState { } fn exec_p_cmd(&mut self, ui: &mut MutexGuard) -> io::Result { + let count = match self.count { + Some(count) => count, + None => 1, + }; + for _ in 0..count { + self.paste(ui)?; + } self.reset(); - self.paste(ui).map(|_| WarpUiCallBackType::None) + return Ok(WarpUiCallBackType::None); } } pub trait StateMachine { fn handle(&mut self, ui: &mut MutexGuard) -> io::Result; - fn exit(&mut self) -> io::Result<()>; + fn exit(&mut self, callback: WarpUiCallBackType) -> io::Result; + fn reset(&mut self); } impl StateMachine for NormalState { + fn reset(&mut self) { + self.cmdchar = None; + self.count = None; + self.count0 = false; + self.start_pos = None; + self.end_pos = None; + self.cmdbuf.clear(); + self.buf_op_arg = None; + } + fn handle(&mut self, ui: &mut MutexGuard) -> io::Result { if self.cmdchar.is_none() { return Ok(WarpUiCallBackType::None); @@ -694,8 +701,8 @@ impl StateMachine for NormalState { } } - fn exit(&mut self) -> io::Result<()> { + fn exit(&mut self, callback: WarpUiCallBackType) -> io::Result { self.reset(); - Ok(()) + Ok(callback) } } From a2e72daf911ac90b88ba77a40da19a8743472827 Mon Sep 17 00:00:00 2001 From: trv3wood <202330452442@mail.scut.edu.cn> Date: Tue, 17 Sep 2024 10:07:24 +0000 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A4=8D=E5=88=B6=E7=B2=98=E8=B4=B4?= =?UTF-8?q?=E6=8B=AC=E5=8F=B7=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 + src/utils/buffer.rs | 52 ++++++++++++ src/utils/reg.rs | 11 ++- src/utils/ui/mode/matching.rs | 151 ++++++++++++++++++++++++++++++++++ src/utils/ui/mode/mod.rs | 1 + src/utils/ui/mode/normal.rs | 130 ++++++++++++++++------------- 6 files changed, 285 insertions(+), 62 deletions(-) create mode 100644 src/utils/ui/mode/matching.rs diff --git a/Cargo.toml b/Cargo.toml index 4479d9e..8f4bd13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,5 @@ serde_yaml = "0.9" # 定义标志位 bitflags = "2.4.2" +# 正则表达式 +regex = "1.10.6" diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 77cccfb..60f43b7 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -271,6 +271,58 @@ impl EditBuffer { ret } + #[inline] + pub fn get_range_str(&self, start_pos: (u16, u16), end_pos: (u16, u16)) -> String { + let buf = self.buf.read().unwrap(); + let mut ret = String::new(); + let start = self.offset.load(Ordering::SeqCst) + start_pos.1 as usize; + let end = self.offset.load(Ordering::SeqCst) + end_pos.1 as usize; + // 从起始行到结束行 + for (idx, line) in buf.iter().enumerate().skip(start).take(end - start + 1) { + if idx == start { + ret.push_str(&String::from_utf8_lossy(&line[start_pos.0 as usize..]).to_string()); + } else if idx == end { + ret.push_str(&String::from_utf8_lossy(&line[..end_pos.0 as usize]).to_string()); + } else { + ret.push_str(&String::from_utf8_lossy(&line).to_string()); + } + } + ret + } + + #[inline] + pub fn get_offset_by_pos(&self, x: u16, y: u16) -> usize { + let mut offset = 0; + let mut abs_y = self.offset(); + let buf = self.buf.read().unwrap(); + for line in buf[abs_y..].iter() { + if abs_y == y as usize + self.offset() { + offset += x as usize; + break; + } + offset += line.size(); + abs_y += 1; + } + offset + } + + pub fn get_pos_by_offset(&self, offset: usize) -> (u16, u16) { + let mut x = 0; + let mut y = 0; + let abs_y = self.offset(); + let buf = self.buf.read().unwrap(); + let mut offset = offset; + for line in buf[abs_y..].iter() { + if offset < line.size() { + x = offset as u16; + break; + } + offset -= line.size(); + y += 1; + } + (x, y) + } + #[inline] pub fn all_buffer(&self) -> Vec { self.buf.read().unwrap().clone() diff --git a/src/utils/reg.rs b/src/utils/reg.rs index fdd3cb3..b987994 100644 --- a/src/utils/reg.rs +++ b/src/utils/reg.rs @@ -19,12 +19,15 @@ impl Register { } pub fn from(lines: Vec) -> Register { - Register { - text: Vec::from(lines), - } + Register { text: lines } } pub fn copy(&mut self, lines: Vec) { - self.text = Vec::from(lines); + self.text.clear(); + self.text = lines; + } + + pub fn push(&mut self, line: &str) { + self.text.push(LineBuffer::new(line.as_bytes().to_vec())); } } diff --git a/src/utils/ui/mode/matching.rs b/src/utils/ui/mode/matching.rs new file mode 100644 index 0000000..5be1670 --- /dev/null +++ b/src/utils/ui/mode/matching.rs @@ -0,0 +1,151 @@ +use lazy_static::lazy_static; +use regex::Regex; +use std::{collections::HashMap, sync::MutexGuard}; + +use crate::utils::ui::uicore::{UiCore, CONTENT_WINSIZE}; + +lazy_static! { + // value is regex + pub static ref PAIRING: HashMap = { + let mut m = HashMap::new(); + m.insert('(', Regex::new(r"(?s)\(.*?").unwrap()); + m.insert('[', Regex::new(r"(?s)\[.*?").unwrap()); + m.insert('{', Regex::new(r"(?s)\{.*?").unwrap()); + m.insert('<', Regex::new(r"(?s)<.*?").unwrap()); + m.insert('\'', Regex::new(r"(?s)\'(.*?)\'").unwrap()); + m.insert('"', Regex::new(r#"(?s)"(.*?)""#).unwrap()); + m.insert('`', Regex::new(r"(?s)`(.*?)`").unwrap()); + m.insert(')', Regex::new(r"\)").unwrap()); + m.insert(']', Regex::new(r"\]").unwrap()); + m.insert('}', Regex::new(r"\}").unwrap()); + m.insert('>', Regex::new(r"\>").unwrap()); + m + }; +} + +fn is_left(pat: char) -> bool { + let left = ['(', '[', '{', '<']; + left.contains(&pat) +} + +fn is_right(pat: char) -> bool { + let right = [')', ']', '}', '>']; + right.contains(&pat) +} + +fn is_quote(pat: char) -> bool { + let quote = ['\'', '"', '`']; + quote.contains(&pat) +} + +fn get_pair(pat: char) -> char { + match pat { + '(' => ')', + '[' => ']', + '{' => '}', + '<' => '>', + '\'' => '\'', + '"' => '"', + '`' => '`', + ')' => '(', + ']' => '[', + '}' => '{', + '>' => '<', + _ => unreachable!(), + } +} +/// 获取括号文本 +pub fn find_pair(ui: &mut MutexGuard, pat: u8) -> Option { + let win_rows = CONTENT_WINSIZE.read().unwrap().rows; + // 搜索范围为整个屏幕 + let content = ui.buffer.get_range_str((0, 0), (0, win_rows - 1)); + let x = ui.cursor.x(); + let y = ui.cursor.y(); + let offset = ui.buffer.get_offset_by_pos(x, y); + get_nested_pair(&content, pat as char, offset, ui) +} + +/// 获取匹配的括号 +/// @param text: 文本 +/// @param pat: 括号 +/// @param pos: 光标位置转换后的偏移量 +/// @return: 匹配的括号文本 +fn get_nested_pair( + text: &str, + pat: char, + pos: usize, + ui: &mut MutexGuard, +) -> Option { + let regex = PAIRING.get(&pat)?; + let mtch = regex.find_at(text, pos); + + if let Some(m) = mtch { + let (start, end) = (m.start(), m.end()); + let new_cursor_start = ui.buffer.get_pos_by_offset(start); + + match pat { + _ if is_quote(pat) => { + ui.cursor + .move_to(new_cursor_start.0, new_cursor_start.1) + .ok()?; + return Some(m.as_str().to_string()); + } + _ if is_left(pat) => { + ui.cursor + .move_to(new_cursor_start.0, new_cursor_start.1) + .ok()?; + return find_matching_right(text, pat, start); + } + _ if is_right(pat) => { + return find_matching_left(text, pat, end, ui); + } + _ => None, + } + } else { + None + } +} + +fn find_matching_right(text: &str, left_pat: char, start: usize) -> Option { + let right_pat = get_pair(left_pat); + let mut stack = Vec::new(); + let end; + + for (idx, c) in text[start..].chars().enumerate() { + if c == left_pat { + stack.push(c); + } else if c == right_pat { + stack.pop(); + if stack.is_empty() { + end = idx + start; + return Some(text[start..=end].to_string()); + } + } + } + None +} + +fn find_matching_left( + text: &str, + right_pat: char, + end: usize, + ui: &mut MutexGuard, +) -> Option { + let left_pat = get_pair(right_pat); + let mut stack = Vec::new(); + let chars: Vec = text[..=end].chars().collect(); + + for (idx, c) in chars.iter().enumerate().rev() { + if *c == right_pat { + stack.push(*c); + } else if *c == left_pat { + stack.pop(); + if stack.is_empty() { + let new_cursor = ui.buffer.get_pos_by_offset(idx); + ui.cursor.move_to(new_cursor.0, new_cursor.1).ok()?; + return Some(text[idx..end].to_string()); + } + } + } + None +} diff --git a/src/utils/ui/mode/mod.rs b/src/utils/ui/mode/mod.rs index cf5a3a5..ade0993 100644 --- a/src/utils/ui/mode/mod.rs +++ b/src/utils/ui/mode/mod.rs @@ -1,3 +1,4 @@ pub mod common; +pub mod matching; pub mod mode; pub mod normal; diff --git a/src/utils/ui/mode/normal.rs b/src/utils/ui/mode/normal.rs index e8cc7e4..9eb5073 100644 --- a/src/utils/ui/mode/normal.rs +++ b/src/utils/ui/mode/normal.rs @@ -30,6 +30,8 @@ use std::io; use std::sync::{Mutex, MutexGuard}; use super::common::CommonOp; +use super::matching::find_pair; +use super::matching::PAIRING; use super::mode::ModeType; #[derive(Debug)] @@ -99,65 +101,50 @@ impl KeyEventCallback for Normal { let mut normal_state = NORMALSTATE.lock().unwrap(); normal_state.cmdbuf.extend_from_slice(data); match data { - b"h" => { - normal_state.on_h_clicked(); - } - b"j" => { - normal_state.on_j_clicked(); - } - b"k" => { - normal_state.on_k_clicked(); - } - b"l" => { - normal_state.on_l_clicked(); - } - b"i" => { - normal_state.on_i_clicked(); - } - b"d" => { - normal_state.on_d_clicked(); - } - [b'1'..=b'9'] => { - normal_state.on_nonzero_clicked(data); - } - b"0" => { - normal_state.on_zero_clicked(); - } - b"w" => { - normal_state.on_w_clicked(); - } - b"g" => { - normal_state.on_g_clicked(ui); - } - b"G" => { - normal_state.on_G_clicked(ui); - } - b"b" => { - normal_state.on_b_clicked(ui); - } + b"h" => normal_state.on_h_clicked(), + + b"j" => normal_state.on_j_clicked(), + + b"k" => normal_state.on_k_clicked(), + + b"l" => normal_state.on_l_clicked(), + + b"i" => normal_state.on_i_clicked(), + + b"d" => normal_state.on_d_clicked(), + + [b'1'..=b'9'] => normal_state.on_nonzero_clicked(data), + + b"0" => normal_state.on_zero_clicked(), + + b"w" => normal_state.on_w_clicked(), + + b"g" => normal_state.on_g_clicked(ui), + + b"G" => normal_state.on_G_clicked(ui), + + b"b" => normal_state.on_b_clicked(ui), + b":" => { if normal_state.cmdchar.is_none() { ui.cursor.store_pos(); return Ok(WarpUiCallBackType::ChangMode(ModeType::LastLine)); } } - b"$" => { - normal_state.on_dollar_clicked(); - } - b"e" => { - normal_state.on_e_clicked(ui); - } - b"f" => { - normal_state.on_f_clicked(); - } - b"F" => { - normal_state.on_F_clicked(); - } - b"x" => { - normal_state.on_x_clicked(); - } + b"$" => normal_state.on_dollar_clicked(), + + b"e" => normal_state.on_e_clicked(ui), + + b"f" => normal_state.on_f_clicked(), + + b"F" => normal_state.on_F_clicked(), + + b"x" => normal_state.on_x_clicked(), + b"y" => normal_state.on_y_clicked(), + b"p" => normal_state.on_p_clicked(), + _ => {} } return normal_state.handle(ui); @@ -322,6 +309,8 @@ impl NormalState { pub fn on_i_clicked(&mut self) { if self.cmdchar.is_none() { self.cmdchar = Some('i'); + } else { + self.buf_op_arg = Some(BufOpArg::Inside); } } pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard) -> io::Result { @@ -546,12 +535,12 @@ impl NormalState { if self.cmdbuf.len() < 2 { return Ok(WarpUiCallBackType::None); } - let to_find = self.cmdbuf.last().unwrap().clone() as char; + let to_find = self.cmdbuf.last().unwrap(); let old_x = ui.cursor.x(); let old_y = ui.cursor.y(); let line = String::from_utf8_lossy(&ui.buffer.get_line(old_y)[old_x as usize..]).to_string(); - let pos = line.find(to_find); + let pos = line.find(*to_find as char); if pos.is_none() { return Ok(WarpUiCallBackType::None); } @@ -573,12 +562,12 @@ impl NormalState { if self.cmdbuf.len() < 2 { return Ok(WarpUiCallBackType::None); } - let to_find = self.cmdbuf.last().unwrap().clone() as char; + let to_find = self.cmdbuf.last().unwrap(); let old_x = ui.cursor.x(); let old_y = ui.cursor.y(); let line = String::from_utf8_lossy(&ui.buffer.get_line(old_y)[..old_x as usize]).to_string(); - let pos = line.rfind(to_find); + let pos = line.rfind(*to_find as char); if pos.is_none() { return Ok(WarpUiCallBackType::None); } @@ -608,9 +597,6 @@ impl NormalState { Some('y') => { self.buf_op_arg = Some(BufOpArg::Line); } - Some('w') => { - self.buf_op_arg = Some(BufOpArg::Word); - } None => { self.cmdchar = Some('y'); } @@ -633,6 +619,34 @@ impl NormalState { ui.register.copy(text_copy); self.reset(); } + Some(BufOpArg::Around) => { + let pat = self.cmdbuf.last().unwrap(); + if PAIRING.contains_key(&(*pat as char)) { + let text = find_pair(ui, *pat); + text.map(|text| { + let sp = text.split('\n').collect::>(); + ui.register.text.clear(); + for line in sp { + ui.register.push(line); + } + }); + self.reset(); + } + } + Some(BufOpArg::Inside) => { + let pat = self.cmdbuf.last().unwrap(); + if PAIRING.contains_key(&(*pat as char)) { + let text = find_pair(ui, *pat); + text.map(|text| { + let sp = text[1..text.len() - 1].split('\n').collect::>(); + ui.register.text.clear(); + for line in sp { + ui.register.push(line); + } + }); + self.reset(); + } + } _ => {} } return Ok(WarpUiCallBackType::None); From 31c1ce238b97b65103cdcda64ef31550d4cf508b Mon Sep 17 00:00:00 2001 From: trv3wood <202330452442@mail.scut.edu.cn> Date: Thu, 19 Sep 2024 13:12:59 +0000 Subject: [PATCH 5/5] =?UTF-8?q?matching::find=5Fpair=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E9=85=8D=E5=AF=B9=E6=8B=AC=E5=8F=B7=E7=9A=84=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/buffer.rs | 16 ++++++++++++-- src/utils/ui/mode/matching.rs | 41 +++++++++++++++++++++++++---------- src/utils/ui/mode/normal.rs | 22 +++++++------------ 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/utils/buffer.rs b/src/utils/buffer.rs index 60f43b7..de7353b 100644 --- a/src/utils/buffer.rs +++ b/src/utils/buffer.rs @@ -257,13 +257,19 @@ impl EditBuffer { let mut ret = Vec::new(); let start = self.offset.load(Ordering::SeqCst) + start_pos.1 as usize; let end = self.offset.load(Ordering::SeqCst) + end_pos.1 as usize; + if start == end { + let line = buf.get(start).unwrap(); + let start_pos = start_pos.0 as usize; + let end_pos = end_pos.0 as usize; + ret.push(LineBuffer::new(line[start_pos..end_pos].to_vec())); + return ret; + } // 从起始行到结束行 for (idx, line) in buf.iter().enumerate().skip(start).take(end - start + 1) { let mut newline = line.clone(); if idx == start { newline.data.drain(0..start_pos.0 as usize); - } - if idx == end { + } else if idx == end { newline.data.drain(end_pos.0 as usize..); } ret.push(newline); @@ -277,6 +283,12 @@ impl EditBuffer { let mut ret = String::new(); let start = self.offset.load(Ordering::SeqCst) + start_pos.1 as usize; let end = self.offset.load(Ordering::SeqCst) + end_pos.1 as usize; + if start == end { + let line = buf.get(start).unwrap(); + let start_pos = start_pos.0 as usize; + let end_pos = end_pos.0 as usize; + return String::from_utf8_lossy(&line[start_pos..end_pos]).to_string(); + } // 从起始行到结束行 for (idx, line) in buf.iter().enumerate().skip(start).take(end - start + 1) { if idx == start { diff --git a/src/utils/ui/mode/matching.rs b/src/utils/ui/mode/matching.rs index 5be1670..3d2ce64 100644 --- a/src/utils/ui/mode/matching.rs +++ b/src/utils/ui/mode/matching.rs @@ -3,9 +3,12 @@ use regex::Regex; use std::{collections::HashMap, sync::MutexGuard}; use crate::utils::ui::uicore::{UiCore, CONTENT_WINSIZE}; - +/// 匹配括号位置 +pub struct PairedPos { + pub start: (u16, u16), + pub end: (u16, u16), +} lazy_static! { - // value is regex pub static ref PAIRING: HashMap = { let mut m = HashMap::new(); m.insert('(', Regex::new(r"(?s)\(.*?").unwrap()); @@ -55,9 +58,10 @@ fn get_pair(pat: char) -> char { } } /// 获取括号文本 -pub fn find_pair(ui: &mut MutexGuard, pat: u8) -> Option { +pub fn find_pair(ui: &mut MutexGuard, pat: u8) -> Option { let win_rows = CONTENT_WINSIZE.read().unwrap().rows; // 搜索范围为整个屏幕 + // 把Vec转换为String,因为Regex::find_at()需要String,而且二维变一维方便迭代 let content = ui.buffer.get_range_str((0, 0), (0, win_rows - 1)); let x = ui.cursor.x(); let y = ui.cursor.y(); @@ -75,7 +79,7 @@ fn get_nested_pair( pat: char, pos: usize, ui: &mut MutexGuard, -) -> Option { +) -> Option { let regex = PAIRING.get(&pat)?; let mtch = regex.find_at(text, pos); @@ -88,13 +92,16 @@ fn get_nested_pair( ui.cursor .move_to(new_cursor_start.0, new_cursor_start.1) .ok()?; - return Some(m.as_str().to_string()); + return Some(PairedPos { + start: new_cursor_start, + end: ui.buffer.get_pos_by_offset(end), + }); } _ if is_left(pat) => { ui.cursor .move_to(new_cursor_start.0, new_cursor_start.1) .ok()?; - return find_matching_right(text, pat, start); + return find_matching_right(text, pat, start, ui); } _ if is_right(pat) => { return find_matching_left(text, pat, end, ui); @@ -106,10 +113,14 @@ fn get_nested_pair( } } -fn find_matching_right(text: &str, left_pat: char, start: usize) -> Option { +fn find_matching_right( + text: &str, + left_pat: char, + start: usize, + ui: &mut MutexGuard, +) -> Option { let right_pat = get_pair(left_pat); let mut stack = Vec::new(); - let end; for (idx, c) in text[start..].chars().enumerate() { if c == left_pat { @@ -117,8 +128,11 @@ fn find_matching_right(text: &str, left_pat: char, start: usize) -> Option, -) -> Option { +) -> Option { let left_pat = get_pair(right_pat); let mut stack = Vec::new(); let chars: Vec = text[..=end].chars().collect(); @@ -143,7 +157,10 @@ fn find_matching_left( if stack.is_empty() { let new_cursor = ui.buffer.get_pos_by_offset(idx); ui.cursor.move_to(new_cursor.0, new_cursor.1).ok()?; - return Some(text[idx..end].to_string()); + return Some(PairedPos { + start: ui.buffer.get_pos_by_offset(idx), + end: ui.buffer.get_pos_by_offset(end), + }); } } } diff --git a/src/utils/ui/mode/normal.rs b/src/utils/ui/mode/normal.rs index 9eb5073..753ec18 100644 --- a/src/utils/ui/mode/normal.rs +++ b/src/utils/ui/mode/normal.rs @@ -622,13 +622,9 @@ impl NormalState { Some(BufOpArg::Around) => { let pat = self.cmdbuf.last().unwrap(); if PAIRING.contains_key(&(*pat as char)) { - let text = find_pair(ui, *pat); - text.map(|text| { - let sp = text.split('\n').collect::>(); - ui.register.text.clear(); - for line in sp { - ui.register.push(line); - } + find_pair(ui, *pat).map(|paired_pos| { + let content = ui.buffer.get_range(paired_pos.start, paired_pos.end); + ui.register.copy(content); }); self.reset(); } @@ -636,13 +632,11 @@ impl NormalState { Some(BufOpArg::Inside) => { let pat = self.cmdbuf.last().unwrap(); if PAIRING.contains_key(&(*pat as char)) { - let text = find_pair(ui, *pat); - text.map(|text| { - let sp = text[1..text.len() - 1].split('\n').collect::>(); - ui.register.text.clear(); - for line in sp { - ui.register.push(line); - } + find_pair(ui, *pat).map(|paired_pos| { + let start = (paired_pos.start.0 + 1, paired_pos.start.1); + let end = (paired_pos.end.0 - 1, paired_pos.end.1); + let content = ui.buffer.get_range(start, end); + ui.register.copy(content); }); self.reset(); }