multiple cursors with primary separate
This commit is contained in:
+38
-4
@@ -1,3 +1,4 @@
|
||||
use core::slice::GetDisjointMutIndex;
|
||||
use std::{fs::File, io::Read, path::PathBuf};
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::{style::Color, text::Span};
|
||||
@@ -12,7 +13,8 @@ pub struct Buffer {
|
||||
pub contents: Vec<u8>,
|
||||
|
||||
pub scroll_position: usize,
|
||||
pub cursor: Cursor,
|
||||
pub primary_cursor: Cursor,
|
||||
pub cursors: Vec<Cursor>,
|
||||
|
||||
pub mode: Mode,
|
||||
pub partial_action: Option<PartialAction>,
|
||||
@@ -79,7 +81,8 @@ impl Buffer {
|
||||
contents,
|
||||
|
||||
scroll_position: 0,
|
||||
cursor: Cursor::default(),
|
||||
primary_cursor: Cursor::default(),
|
||||
cursors: Vec::new(),
|
||||
|
||||
mode: Mode::Normal,
|
||||
partial_action: None,
|
||||
@@ -110,8 +113,13 @@ impl Buffer {
|
||||
if let Some(partial_replace) = self.partial_replace.take() {
|
||||
self.execute_and_add(
|
||||
EditAction::Replace {
|
||||
cursor: self.cursor,
|
||||
old_data: self.contents[self.cursor.range()].into(),
|
||||
primary_cursor: self.primary_cursor,
|
||||
cursors: self.cursors.clone(),
|
||||
primary_old_data: self.contents[self.primary_cursor.range()].to_vec(),
|
||||
old_data: self.cursors
|
||||
.iter()
|
||||
.map(|cursor| self.contents[cursor.range()].to_vec())
|
||||
.collect(),
|
||||
new_byte: partial_replace << 4 | nybble
|
||||
}
|
||||
);
|
||||
@@ -161,6 +169,32 @@ impl Buffer {
|
||||
pub const fn max_contents_index(&self) -> usize {
|
||||
self.contents.len().saturating_sub(1)
|
||||
}
|
||||
|
||||
pub fn combine_cursors_if_overlapping(&mut self) {
|
||||
if self.cursors.is_empty() { return; }
|
||||
|
||||
let mut index = 0;
|
||||
|
||||
while index < self.cursors.len() - 1 {
|
||||
while index < self.cursors.len() - 1 &&
|
||||
self.cursors[index].range().is_overlapping(
|
||||
&self.cursors[index + 1].range())
|
||||
{
|
||||
let next_cursor = self.cursors[index + 1];
|
||||
self.cursors[index].combine_with(next_cursor);
|
||||
self.cursors.remove(index + 1);
|
||||
}
|
||||
|
||||
if self.primary_cursor.range()
|
||||
.is_overlapping(&self.cursors[index].range())
|
||||
{
|
||||
self.primary_cursor.combine_with(self.cursors[index]);
|
||||
self.cursors.remove(index);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nybble_from_hex(hex: char) -> Option<u8> {
|
||||
|
||||
+22
-8
@@ -82,7 +82,7 @@ mod address {
|
||||
}
|
||||
|
||||
mod hex {
|
||||
use std::{borrow::Cow, mem};
|
||||
use std::{borrow::Cow, iter, mem};
|
||||
use itertools::{Itertools, repeat_n};
|
||||
use ratatui::{style::{Color, Style, Stylize}, text::Span};
|
||||
|
||||
@@ -199,7 +199,9 @@ mod hex {
|
||||
byte: u8
|
||||
) -> Span<'static> {
|
||||
if self.partial_action == Some(PartialAction::Replace) &&
|
||||
self.cursor.contains(address).is_some()
|
||||
iter::once(&self.primary_cursor)
|
||||
.chain(&self.cursors)
|
||||
.any(|cursor| cursor.contains(address).is_some())
|
||||
{
|
||||
let replaced_byte = self.partial_replace.unwrap_or(0) << 4;
|
||||
|
||||
@@ -224,7 +226,10 @@ mod hex {
|
||||
_ => Color::Gray
|
||||
};
|
||||
|
||||
match self.cursor.contains(address) {
|
||||
match iter::once(&self.primary_cursor)
|
||||
.chain(&self.cursors)
|
||||
.find_map(|cursor| cursor.contains(address))
|
||||
{
|
||||
Some(InCursor::Head) => span.bg(head_color),
|
||||
Some(InCursor::Rest) => span.bg(Color::select_grey()),
|
||||
None => span,
|
||||
@@ -232,7 +237,10 @@ mod hex {
|
||||
}
|
||||
|
||||
fn render_large_space_before(&self, address: usize) -> Span<'static> {
|
||||
if self.cursor.contains_space_before(address) {
|
||||
if iter::once(&self.primary_cursor)
|
||||
.chain(&self.cursors)
|
||||
.any(|cursor| cursor.contains_space_before(address))
|
||||
{
|
||||
Span {
|
||||
style: Style::new().bg(Color::select_grey()),
|
||||
content: " ".into()
|
||||
@@ -243,7 +251,10 @@ mod hex {
|
||||
}
|
||||
|
||||
fn render_space_before(&self, address: usize) -> Span<'static> {
|
||||
if self.cursor.contains_space_before(address) {
|
||||
if iter::once(&self.primary_cursor)
|
||||
.chain(&self.cursors)
|
||||
.any(|cursor| cursor.contains_space_before(address))
|
||||
{
|
||||
Span {
|
||||
style: Style::new().bg(Color::select_grey()),
|
||||
content: " ".into()
|
||||
@@ -306,7 +317,7 @@ mod hex {
|
||||
}
|
||||
|
||||
mod character_panel {
|
||||
use std::{borrow::Cow, mem};
|
||||
use std::{borrow::Cow, iter, mem};
|
||||
use ratatui::{style::{Color, Style, Stylize}, text::Span};
|
||||
use crate::{buffer::Buffer, cardinality::HasCardinality, cursor::InCursor, custom_greys::CustomGreys, empty_span::empty_span};
|
||||
|
||||
@@ -332,7 +343,10 @@ mod character_panel {
|
||||
|
||||
let span = SPAN_FOR_BYTE[byte as usize].clone();
|
||||
|
||||
match self.cursor.contains(address) {
|
||||
match iter::once(&self.primary_cursor)
|
||||
.chain(&self.cursors)
|
||||
.find_map(|cursor| cursor.contains(address))
|
||||
{
|
||||
Some(InCursor::Head) => span.bg(Color::select_grey()),
|
||||
Some(InCursor::Rest) => span.on_dark_gray(),
|
||||
None => span,
|
||||
@@ -434,7 +448,7 @@ mod extra_statuses {
|
||||
format!("{partial_action} ").into()
|
||||
} else {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let percentage = self.cursor.head as f64 / self.max_contents_index() as f64 * 100.0;
|
||||
let percentage = self.primary_cursor.head as f64 / self.max_contents_index() as f64 * 100.0;
|
||||
|
||||
format!("{partial_action} {percentage:.0}% ").into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user