Implemented proper behavior for deleting/yanking lines into registers and putting them
This commit is contained in:
@@ -14,7 +14,7 @@ use crate::{
|
|||||||
libsh::{
|
libsh::{
|
||||||
error::ShResult,
|
error::ShResult,
|
||||||
term::{Style, Styled},
|
term::{Style, Styled},
|
||||||
}, parse::lex::{LexFlags, LexStream, Tk, TkFlags, TkRule}, prelude::*, prompt::readline::{markers, register::write_register}, state::read_shopts
|
}, parse::lex::{LexFlags, LexStream, Tk, TkFlags, TkRule}, prelude::*, prompt::readline::{markers, register::{write_register, RegisterContent}}, state::read_shopts
|
||||||
};
|
};
|
||||||
|
|
||||||
const PUNCTUATION: [&str; 3] = ["?", "!", "."];
|
const PUNCTUATION: [&str; 3] = ["?", "!", "."];
|
||||||
@@ -2441,7 +2441,7 @@ impl LineBuf {
|
|||||||
do_indent = read_shopts(|o| o.prompt.auto_indent);
|
do_indent = read_shopts(|o| o.prompt.auto_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
let register_text = if verb == Verb::Yank {
|
let text = if verb == Verb::Yank {
|
||||||
self
|
self
|
||||||
.slice(start..end)
|
.slice(start..end)
|
||||||
.map(|c| c.to_string())
|
.map(|c| c.to_string())
|
||||||
@@ -2451,7 +2451,16 @@ impl LineBuf {
|
|||||||
self.update_graphemes();
|
self.update_graphemes();
|
||||||
drained
|
drained
|
||||||
};
|
};
|
||||||
register.write_to_register(register_text);
|
let is_linewise = matches!(
|
||||||
|
motion,
|
||||||
|
MotionKind::InclusiveWithTargetCol(..) | MotionKind::ExclusiveWithTargetCol(..)
|
||||||
|
);
|
||||||
|
let register_content = if is_linewise {
|
||||||
|
RegisterContent::Line(text)
|
||||||
|
} else {
|
||||||
|
RegisterContent::Span(text)
|
||||||
|
};
|
||||||
|
register.write_to_register(register_content);
|
||||||
self.cursor.set(start);
|
self.cursor.set(start);
|
||||||
if do_indent {
|
if do_indent {
|
||||||
self.calc_indent_level();
|
self.calc_indent_level();
|
||||||
@@ -2630,22 +2639,46 @@ impl LineBuf {
|
|||||||
let Some(content) = register.read_from_register() else {
|
let Some(content) = register.read_from_register() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
if content.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
if let Some(range) = self.select_range {
|
if let Some(range) = self.select_range {
|
||||||
let register_text = self.drain_inclusive(range.0..=range.1);
|
let register_text = self.drain_inclusive(range.0..=range.1);
|
||||||
write_register(None, register_text); // swap deleted text into register
|
write_register(None, RegisterContent::Span(register_text)); // swap deleted text into register
|
||||||
|
|
||||||
self.insert_str_at(range.0, &content);
|
let text = content.as_str();
|
||||||
self.cursor.set(range.0 + content.chars().count());
|
self.insert_str_at(range.0, text);
|
||||||
|
self.cursor.set(range.0 + content.char_count());
|
||||||
self.select_range = None;
|
self.select_range = None;
|
||||||
self.update_graphemes();
|
self.update_graphemes();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
match content {
|
||||||
|
RegisterContent::Span(ref text) => {
|
||||||
let insert_idx = match anchor {
|
let insert_idx = match anchor {
|
||||||
Anchor::After => self.cursor.ret_add(1),
|
Anchor::After => self.cursor.ret_add(1),
|
||||||
Anchor::Before => self.cursor.get(),
|
Anchor::Before => self.cursor.get(),
|
||||||
};
|
};
|
||||||
self.insert_str_at(insert_idx, &content);
|
self.insert_str_at(insert_idx, text);
|
||||||
self.cursor.add(content.len().saturating_sub(1));
|
self.cursor.add(text.len().saturating_sub(1));
|
||||||
|
}
|
||||||
|
RegisterContent::Line(ref text) => {
|
||||||
|
let insert_idx = match anchor {
|
||||||
|
Anchor::After => self.end_of_line(),
|
||||||
|
Anchor::Before => self.start_of_line(),
|
||||||
|
};
|
||||||
|
let needs_newline = self.grapheme_before(insert_idx).is_some_and(|gr| gr != "\n");
|
||||||
|
if needs_newline {
|
||||||
|
let full = format!("\n{}", text);
|
||||||
|
self.insert_str_at(insert_idx, &full);
|
||||||
|
self.cursor.set(insert_idx + 1);
|
||||||
|
} else {
|
||||||
|
self.insert_str_at(insert_idx, text);
|
||||||
|
self.cursor.set(insert_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegisterContent::Empty => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Verb::SwapVisualAnchor => {
|
Verb::SwapVisualAnchor => {
|
||||||
if let Some((start, end)) = self.select_range()
|
if let Some((start, end)) = self.select_range()
|
||||||
|
|||||||
@@ -1,26 +1,84 @@
|
|||||||
use std::sync::Mutex;
|
use std::{fmt::Display, sync::Mutex};
|
||||||
|
|
||||||
pub static REGISTERS: Mutex<Registers> = Mutex::new(Registers::new());
|
pub static REGISTERS: Mutex<Registers> = Mutex::new(Registers::new());
|
||||||
|
|
||||||
pub fn read_register(ch: Option<char>) -> Option<String> {
|
pub fn read_register(ch: Option<char>) -> Option<RegisterContent> {
|
||||||
let lock = REGISTERS.lock().unwrap();
|
let lock = REGISTERS.lock().unwrap();
|
||||||
lock.get_reg(ch).map(|r| r.buf().clone())
|
lock.get_reg(ch).map(|r| r.content().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_register(ch: Option<char>, buf: String) {
|
pub fn write_register(ch: Option<char>, buf: RegisterContent) {
|
||||||
let mut lock = REGISTERS.lock().unwrap();
|
let mut lock = REGISTERS.lock().unwrap();
|
||||||
if let Some(r) = lock.get_reg_mut(ch) {
|
if let Some(r) = lock.get_reg_mut(ch) {
|
||||||
r.write(buf)
|
r.write(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append_register(ch: Option<char>, buf: String) {
|
pub fn append_register(ch: Option<char>, buf: RegisterContent) {
|
||||||
let mut lock = REGISTERS.lock().unwrap();
|
let mut lock = REGISTERS.lock().unwrap();
|
||||||
if let Some(r) = lock.get_reg_mut(ch) {
|
if let Some(r) = lock.get_reg_mut(ch) {
|
||||||
r.append(buf)
|
r.append(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub enum RegisterContent {
|
||||||
|
Span(String),
|
||||||
|
Line(String),
|
||||||
|
#[default]
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RegisterContent {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Span(s) => write!(f, "{}", s),
|
||||||
|
Self::Line(s) => write!(f, "{}", s),
|
||||||
|
Self::Empty => write!(f, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterContent {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Span(s) => s.clear(),
|
||||||
|
Self::Line(s) => s.clear(),
|
||||||
|
Self::Empty => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Span(s) => s.len(),
|
||||||
|
Self::Line(s) => s.len(),
|
||||||
|
Self::Empty => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Span(s) => s.is_empty(),
|
||||||
|
Self::Line(s) => s.is_empty(),
|
||||||
|
Self::Empty => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_line(&self) -> bool {
|
||||||
|
matches!(self, Self::Line(_))
|
||||||
|
}
|
||||||
|
pub fn is_span(&self) -> bool {
|
||||||
|
matches!(self, Self::Span(_))
|
||||||
|
}
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Span(s) => s,
|
||||||
|
Self::Line(s) => s,
|
||||||
|
Self::Empty => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn char_count(&self) -> usize {
|
||||||
|
self.as_str().chars().count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Registers {
|
pub struct Registers {
|
||||||
default: Register,
|
default: Register,
|
||||||
@@ -55,33 +113,33 @@ pub struct Registers {
|
|||||||
impl Registers {
|
impl Registers {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
default: Register(String::new()),
|
default: Register::new(),
|
||||||
a: Register(String::new()),
|
a: Register::new(),
|
||||||
b: Register(String::new()),
|
b: Register::new(),
|
||||||
c: Register(String::new()),
|
c: Register::new(),
|
||||||
d: Register(String::new()),
|
d: Register::new(),
|
||||||
e: Register(String::new()),
|
e: Register::new(),
|
||||||
f: Register(String::new()),
|
f: Register::new(),
|
||||||
g: Register(String::new()),
|
g: Register::new(),
|
||||||
h: Register(String::new()),
|
h: Register::new(),
|
||||||
i: Register(String::new()),
|
i: Register::new(),
|
||||||
j: Register(String::new()),
|
j: Register::new(),
|
||||||
k: Register(String::new()),
|
k: Register::new(),
|
||||||
l: Register(String::new()),
|
l: Register::new(),
|
||||||
m: Register(String::new()),
|
m: Register::new(),
|
||||||
n: Register(String::new()),
|
n: Register::new(),
|
||||||
o: Register(String::new()),
|
o: Register::new(),
|
||||||
p: Register(String::new()),
|
p: Register::new(),
|
||||||
q: Register(String::new()),
|
q: Register::new(),
|
||||||
r: Register(String::new()),
|
r: Register::new(),
|
||||||
s: Register(String::new()),
|
s: Register::new(),
|
||||||
t: Register(String::new()),
|
t: Register::new(),
|
||||||
u: Register(String::new()),
|
u: Register::new(),
|
||||||
v: Register(String::new()),
|
v: Register::new(),
|
||||||
w: Register(String::new()),
|
w: Register::new(),
|
||||||
x: Register(String::new()),
|
x: Register::new(),
|
||||||
y: Register(String::new()),
|
y: Register::new(),
|
||||||
z: Register(String::new()),
|
z: Register::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_reg(&self, ch: Option<char>) -> Option<&Register> {
|
pub fn get_reg(&self, ch: Option<char>) -> Option<&Register> {
|
||||||
@@ -155,18 +213,39 @@ impl Registers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
pub struct Register(String);
|
pub struct Register {
|
||||||
|
content: RegisterContent,
|
||||||
|
}
|
||||||
|
|
||||||
impl Register {
|
impl Register {
|
||||||
pub fn buf(&self) -> &String {
|
pub const fn new() -> Self {
|
||||||
&self.0
|
Self {
|
||||||
|
content: RegisterContent::Span(String::new()),
|
||||||
}
|
}
|
||||||
pub fn write(&mut self, buf: String) {
|
|
||||||
self.0 = buf
|
|
||||||
}
|
}
|
||||||
pub fn append(&mut self, buf: String) {
|
pub fn content(&self) -> &RegisterContent {
|
||||||
self.0.push_str(&buf)
|
&self.content
|
||||||
|
}
|
||||||
|
pub fn write(&mut self, buf: RegisterContent) {
|
||||||
|
self.content = buf
|
||||||
|
}
|
||||||
|
pub fn append(&mut self, buf: RegisterContent) {
|
||||||
|
match buf {
|
||||||
|
RegisterContent::Empty => {}
|
||||||
|
RegisterContent::Span(ref s) | RegisterContent::Line(ref s) => match &mut self.content {
|
||||||
|
RegisterContent::Empty => self.content = buf,
|
||||||
|
RegisterContent::Span(existing) => existing.push_str(s),
|
||||||
|
RegisterContent::Line(existing) => existing.push_str(s),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0.clear()
|
self.content.clear()
|
||||||
|
}
|
||||||
|
pub fn is_line(&self) -> bool {
|
||||||
|
self.content.is_line()
|
||||||
|
}
|
||||||
|
pub fn is_span(&self) -> bool {
|
||||||
|
self.content.is_span()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use super::register::{append_register, read_register, write_register};
|
use super::register::{append_register, read_register, write_register, RegisterContent};
|
||||||
|
|
||||||
//TODO: write tests that take edit results and cursor positions from actual
|
//TODO: write tests that take edit results and cursor positions from actual
|
||||||
// neovim edits and test them against the behavior of this editor
|
// neovim edits and test them against the behavior of this editor
|
||||||
@@ -35,14 +35,14 @@ impl RegisterName {
|
|||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.count
|
self.count
|
||||||
}
|
}
|
||||||
pub fn write_to_register(&self, buf: String) {
|
pub fn write_to_register(&self, buf: RegisterContent) {
|
||||||
if self.append {
|
if self.append {
|
||||||
append_register(self.name, buf);
|
append_register(self.name, buf);
|
||||||
} else {
|
} else {
|
||||||
write_register(self.name, buf);
|
write_register(self.name, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn read_from_register(&self) -> Option<String> {
|
pub fn read_from_register(&self) -> Option<RegisterContent> {
|
||||||
read_register(self.name)
|
read_register(self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user