Handle paste correctly when there is only one full-line in the clipboard
This commit is contained in:
parent
e082935076
commit
4a395314b2
1 changed files with 95 additions and 68 deletions
|
@ -18,6 +18,7 @@ use smol::Timer;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
|
iter::FromIterator,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
@ -499,10 +500,13 @@ impl BufferView {
|
||||||
selection.start = buffer.anchor_before(start).unwrap();
|
selection.start = buffer.anchor_before(start).unwrap();
|
||||||
selection.end = buffer.anchor_after(end).unwrap();
|
selection.end = buffer.anchor_after(end).unwrap();
|
||||||
}
|
}
|
||||||
let prev_len = text.len();
|
let mut len = 0;
|
||||||
text.extend(buffer.text_for_range(start..end).unwrap());
|
for ch in buffer.text_for_range(start..end).unwrap() {
|
||||||
|
text.push(ch);
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
clipboard_selections.push(ClipboardSelection {
|
clipboard_selections.push(ClipboardSelection {
|
||||||
len: text.len() - prev_len,
|
len,
|
||||||
is_entire_line,
|
is_entire_line,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -521,7 +525,7 @@ impl BufferView {
|
||||||
let max_point = buffer.max_point();
|
let max_point = buffer.max_point();
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
let selections = self.selections(ctx.app());
|
let selections = self.selections(ctx.app());
|
||||||
let mut selection_lengths = Vec::with_capacity(selections.len());
|
let mut clipboard_selections = Vec::with_capacity(selections.len());
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
let mut start = selection.start.to_point(buffer).expect("invalid start");
|
let mut start = selection.start.to_point(buffer).expect("invalid start");
|
||||||
let mut end = selection.end.to_point(buffer).expect("invalid end");
|
let mut end = selection.end.to_point(buffer).expect("invalid end");
|
||||||
|
@ -530,86 +534,109 @@ impl BufferView {
|
||||||
start = Point::new(start.row, 0);
|
start = Point::new(start.row, 0);
|
||||||
end = cmp::min(max_point, Point::new(start.row + 1, 0));
|
end = cmp::min(max_point, Point::new(start.row + 1, 0));
|
||||||
}
|
}
|
||||||
let prev_len = text.len();
|
let mut len = 0;
|
||||||
text.extend(buffer.text_for_range(start..end).unwrap());
|
for ch in buffer.text_for_range(start..end).unwrap() {
|
||||||
selection_lengths.push(ClipboardSelection {
|
text.push(ch);
|
||||||
len: text.len() - prev_len,
|
len += 1;
|
||||||
|
}
|
||||||
|
clipboard_selections.push(ClipboardSelection {
|
||||||
|
len,
|
||||||
is_entire_line,
|
is_entire_line,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.app_mut()
|
ctx.app_mut()
|
||||||
.write_to_clipboard(ClipboardItem::new(text).with_metadata(selection_lengths));
|
.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paste(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
pub fn paste(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||||
if let Some(item) = ctx.app_mut().read_from_clipboard() {
|
if let Some(item) = ctx.app_mut().read_from_clipboard() {
|
||||||
let clipboard_text = item.text();
|
let clipboard_text = item.text();
|
||||||
if let Some(clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
|
if let Some(clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
|
||||||
// If there are the same number of selections as there were at the
|
let selections_len = self.selections(ctx.app()).len();
|
||||||
// time that this clipboard data was written, then paste one slice of the
|
if clipboard_selections.len() == selections_len {
|
||||||
// clipboard text into each of the current selections.
|
// If there are the same number of selections as there were at the time that
|
||||||
let selections = self.selections(ctx.app()).to_vec();
|
// this clipboard data was written, then paste one slice of the clipboard text
|
||||||
if clipboard_selections.len() == selections.len() {
|
// into each of the current selections.
|
||||||
self.start_transaction(ctx);
|
self.multiline_paste(clipboard_text.chars(), clipboard_selections.iter(), ctx);
|
||||||
let mut new_selections = Vec::with_capacity(selections.len());
|
} else if clipboard_selections.len() == 1 && clipboard_selections[0].is_entire_line
|
||||||
let mut clipboard_offset = 0;
|
{
|
||||||
for (i, selection) in selections.iter().enumerate() {
|
// If there was only one selection in the clipboard but it spanned the whole
|
||||||
let clipboard_selection = &clipboard_selections[i];
|
// line, then paste it over and over into each of the current selections so that
|
||||||
let clipboard_slice = &clipboard_text
|
// we can position it before the selections that are empty.
|
||||||
[clipboard_offset..(clipboard_offset + clipboard_selection.len)];
|
self.multiline_paste(
|
||||||
clipboard_offset = clipboard_offset + clipboard_selection.len;
|
clipboard_text.chars().cycle(),
|
||||||
|
clipboard_selections.iter().cycle(),
|
||||||
self.buffer.update(ctx, |buffer, ctx| {
|
ctx,
|
||||||
let selection_start = selection.start.to_point(buffer).unwrap();
|
);
|
||||||
let selection_end = selection.end.to_point(buffer).unwrap();
|
} else {
|
||||||
let char_count = clipboard_slice.chars().count();
|
self.insert(clipboard_text, ctx);
|
||||||
let max_point = buffer.max_point();
|
|
||||||
|
|
||||||
// If the corresponding selection was empty when this slice of the
|
|
||||||
// clipboard text was written, then the entire line containing the
|
|
||||||
// selection was copied. If this selection is also currently empty,
|
|
||||||
// then paste the line before the current line of the buffer.
|
|
||||||
let anchor;
|
|
||||||
if selection_start == selection_end
|
|
||||||
&& clipboard_selection.is_entire_line
|
|
||||||
{
|
|
||||||
let start_point = Point::new(selection_start.row, 0);
|
|
||||||
let start = start_point.to_offset(buffer).unwrap();
|
|
||||||
let new_position = cmp::min(
|
|
||||||
max_point,
|
|
||||||
Point::new(start_point.row + 1, selection_start.column),
|
|
||||||
);
|
|
||||||
buffer
|
|
||||||
.edit(Some(start..start), clipboard_slice, Some(ctx))
|
|
||||||
.unwrap();
|
|
||||||
anchor = buffer.anchor_before(new_position).unwrap();
|
|
||||||
} else {
|
|
||||||
let start = selection.start.to_offset(buffer).unwrap();
|
|
||||||
let end = selection.end.to_offset(buffer).unwrap();
|
|
||||||
buffer
|
|
||||||
.edit(Some(start..end), clipboard_slice, Some(ctx))
|
|
||||||
.unwrap();
|
|
||||||
anchor = buffer.anchor_before(start + char_count).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
new_selections.push(Selection {
|
|
||||||
start: anchor.clone(),
|
|
||||||
end: anchor,
|
|
||||||
reversed: false,
|
|
||||||
goal_column: None,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.update_selections(new_selections, ctx);
|
|
||||||
self.end_transaction(ctx);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.insert(clipboard_text, ctx);
|
||||||
}
|
}
|
||||||
self.insert(item.text(), ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn multiline_paste<'a>(
|
||||||
|
&mut self,
|
||||||
|
mut clipboard_text: impl Iterator<Item = char>,
|
||||||
|
clipboard_selections: impl Iterator<Item = &'a ClipboardSelection>,
|
||||||
|
ctx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
self.start_transaction(ctx);
|
||||||
|
let selections = self.selections(ctx.app()).to_vec();
|
||||||
|
let mut new_selections = Vec::with_capacity(selections.len());
|
||||||
|
let mut clipboard_offset = 0;
|
||||||
|
for (selection, clipboard_selection) in selections.iter().zip(clipboard_selections) {
|
||||||
|
let clipboard_slice =
|
||||||
|
String::from_iter(clipboard_text.by_ref().take(clipboard_selection.len));
|
||||||
|
clipboard_offset = clipboard_offset + clipboard_selection.len;
|
||||||
|
|
||||||
|
self.buffer.update(ctx, |buffer, ctx| {
|
||||||
|
let selection_start = selection.start.to_point(buffer).unwrap();
|
||||||
|
let selection_end = selection.end.to_point(buffer).unwrap();
|
||||||
|
let max_point = buffer.max_point();
|
||||||
|
|
||||||
|
// If the corresponding selection was empty when this slice of the
|
||||||
|
// clipboard text was written, then the entire line containing the
|
||||||
|
// selection was copied. If this selection is also currently empty,
|
||||||
|
// then paste the line before the current line of the buffer.
|
||||||
|
let anchor;
|
||||||
|
if selection_start == selection_end && clipboard_selection.is_entire_line {
|
||||||
|
let start_point = Point::new(selection_start.row, 0);
|
||||||
|
let start = start_point.to_offset(buffer).unwrap();
|
||||||
|
let new_position = cmp::min(
|
||||||
|
max_point,
|
||||||
|
Point::new(start_point.row + 1, selection_start.column),
|
||||||
|
);
|
||||||
|
buffer
|
||||||
|
.edit(Some(start..start), clipboard_slice, Some(ctx))
|
||||||
|
.unwrap();
|
||||||
|
anchor = buffer.anchor_before(new_position).unwrap();
|
||||||
|
} else {
|
||||||
|
let start = selection.start.to_offset(buffer).unwrap();
|
||||||
|
let end = selection.end.to_offset(buffer).unwrap();
|
||||||
|
buffer
|
||||||
|
.edit(Some(start..end), clipboard_slice, Some(ctx))
|
||||||
|
.unwrap();
|
||||||
|
anchor = buffer
|
||||||
|
.anchor_before(start + clipboard_selection.len)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_selections.push(Selection {
|
||||||
|
start: anchor.clone(),
|
||||||
|
end: anchor,
|
||||||
|
reversed: false,
|
||||||
|
goal_column: None,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.update_selections(new_selections, ctx);
|
||||||
|
self.end_transaction(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
pub fn undo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||||
self.buffer
|
self.buffer
|
||||||
.update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));
|
.update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue