Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat clipboard #20

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ serde_yaml = "0.9"

# 定义标志位
bitflags = "2.4.2"
# 正则表达式
regex = "1.10.6"
88 changes: 86 additions & 2 deletions src/utils/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,90 @@ impl EditBuffer {
}
}

#[inline]
pub fn get_range(&self, start_pos: (u16, u16), end_pos: (u16, u16)) -> Vec<LineBuffer> {
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;
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);
} else if idx == end {
newline.data.drain(end_pos.0 as usize..);
}
ret.push(newline);
}
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;
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 {
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<LineBuffer> {
self.buf.read().unwrap().clone()
Expand Down Expand Up @@ -352,9 +436,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;
}

Expand Down
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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;
Expand Down
33 changes: 33 additions & 0 deletions src/utils/reg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use super::buffer::LineBuffer;

#[derive(Debug)]
pub struct Register {
pub text: Vec<LineBuffer>,
}

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<LineBuffer>) -> Register {
Register { text: lines }
}

pub fn copy(&mut self, lines: Vec<LineBuffer>) {
self.text.clear();
self.text = lines;
}

pub fn push(&mut self, line: &str) {
self.text.push(LineBuffer::new(line.as_bytes().to_vec()));
}
}
31 changes: 30 additions & 1 deletion src/utils/ui/mode/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io, sync::MutexGuard};
use std::{io, sync::MutexGuard, usize};

use crate::utils::{
terminal::TermManager,
Expand Down Expand Up @@ -118,6 +118,35 @@ pub trait CommonOp: KeyEventCallback {
return (next_end_pos.min(linesize as u16 - 1), abs_y);
}

fn paste(&self, ui: &mut MutexGuard<UiCore>) -> 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(())
}
/// 定位到下一个单词的首字母,返回绝对坐标
fn locate_next_word(&self, ui: &mut MutexGuard<UiCore>, abs_pos: (u16, u16)) -> (u16, u16) {
let linesize = ui.buffer.get_linesize_abs(abs_pos.1) as usize;
Expand Down
168 changes: 168 additions & 0 deletions src/utils/ui/mode/matching.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use lazy_static::lazy_static;
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! {
pub static ref PAIRING: HashMap<char, Regex> = {
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<UiCore>, pat: u8) -> Option<PairedPos> {
let win_rows = CONTENT_WINSIZE.read().unwrap().rows;
// 搜索范围为整个屏幕
// 把Vec<LineBuffer>转换为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();
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<UiCore>,
) -> Option<PairedPos> {
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(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, ui);
}
_ 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,
ui: &mut MutexGuard<UiCore>,
) -> Option<PairedPos> {
let right_pat = get_pair(left_pat);
let mut stack = Vec::new();

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() {
let end = idx + start;
return Some(PairedPos {
start: ui.buffer.get_pos_by_offset(start),
end: ui.buffer.get_pos_by_offset(end + 1),
});
}
}
}
None
}

fn find_matching_left(
text: &str,
right_pat: char,
end: usize,
ui: &mut MutexGuard<UiCore>,
) -> Option<PairedPos> {
let left_pat = get_pair(right_pat);
let mut stack = Vec::new();
let chars: Vec<char> = 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(PairedPos {
start: ui.buffer.get_pos_by_offset(idx),
end: ui.buffer.get_pos_by_offset(end),
});
}
}
}
None
}
1 change: 1 addition & 0 deletions src/utils/ui/mode/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod common;
pub mod matching;
pub mod mode;
pub mod normal;
pub mod state;
Loading
Loading