Detailed changes
@@ -14434,15 +14434,16 @@ impl ViewInputHandler for Editor {
fn text_for_range(
&mut self,
range_utf16: Range<usize>,
+ adjusted_range: &mut Option<Range<usize>>,
cx: &mut ViewContext<Self>,
) -> Option<String> {
- Some(
- self.buffer
- .read(cx)
- .read(cx)
- .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
- .collect(),
- )
+ let snapshot = self.buffer.read(cx).read(cx);
+ let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
+ let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
+ if (start.0..end.0) != range_utf16 {
+ adjusted_range.replace(start.0..end.0);
+ }
+ Some(snapshot.text_for_range(start..end).collect())
}
fn selected_text_range(
@@ -15,7 +15,10 @@ actions!(
SelectAll,
Home,
End,
- ShowCharacterPalette
+ ShowCharacterPalette,
+ Paste,
+ Cut,
+ Copy,
]
);
@@ -107,6 +110,28 @@ impl TextInput {
cx.show_character_palette();
}
+ fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
+ if let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) {
+ self.replace_text_in_range(None, &text.replace("\n", " "), cx);
+ }
+ }
+
+ fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+ if !self.selected_range.is_empty() {
+ cx.write_to_clipboard(ClipboardItem::new_string(
+ (&self.content[self.selected_range.clone()]).to_string(),
+ ));
+ }
+ }
+ fn cut(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+ if !self.selected_range.is_empty() {
+ cx.write_to_clipboard(ClipboardItem::new_string(
+ (&self.content[self.selected_range.clone()]).to_string(),
+ ));
+ self.replace_text_in_range(None, "", cx)
+ }
+ }
+
fn move_to(&mut self, offset: usize, cx: &mut ViewContext<Self>) {
self.selected_range = offset..offset;
cx.notify()
@@ -219,9 +244,11 @@ impl ViewInputHandler for TextInput {
fn text_for_range(
&mut self,
range_utf16: Range<usize>,
+ actual_range: &mut Option<Range<usize>>,
_cx: &mut ViewContext<Self>,
) -> Option<String> {
let range = self.range_from_utf16(&range_utf16);
+ actual_range.replace(self.range_to_utf16(&range));
Some(self.content[range].to_string())
}
@@ -497,6 +524,9 @@ impl Render for TextInput {
.on_action(cx.listener(Self::home))
.on_action(cx.listener(Self::end))
.on_action(cx.listener(Self::show_character_palette))
+ .on_action(cx.listener(Self::paste))
+ .on_action(cx.listener(Self::cut))
+ .on_action(cx.listener(Self::copy))
.on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
.on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up))
.on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_mouse_up))
@@ -602,6 +632,9 @@ fn main() {
KeyBinding::new("shift-left", SelectLeft, None),
KeyBinding::new("shift-right", SelectRight, None),
KeyBinding::new("cmd-a", SelectAll, None),
+ KeyBinding::new("cmd-v", Paste, None),
+ KeyBinding::new("cmd-c", Copy, None),
+ KeyBinding::new("cmd-x", Cut, None),
KeyBinding::new("home", Home, None),
KeyBinding::new("end", End, None),
KeyBinding::new("ctrl-cmd-space", ShowCharacterPalette, None),
@@ -9,8 +9,12 @@ use std::ops::Range;
/// See [`InputHandler`] for details on how to implement each method.
pub trait ViewInputHandler: 'static + Sized {
/// See [`InputHandler::text_for_range`] for details
- fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
- -> Option<String>;
+ fn text_for_range(
+ &mut self,
+ range: Range<usize>,
+ adjusted_range: &mut Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<String>;
/// See [`InputHandler::selected_text_range`] for details
fn selected_text_range(
@@ -89,10 +93,12 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
fn text_for_range(
&mut self,
range_utf16: Range<usize>,
+ adjusted_range: &mut Option<Range<usize>>,
cx: &mut WindowContext,
) -> Option<String> {
- self.view
- .update(cx, |view, cx| view.text_for_range(range_utf16, cx))
+ self.view.update(cx, |view, cx| {
+ view.text_for_range(range_utf16, adjusted_range, cx)
+ })
}
fn replace_text_in_range(
@@ -643,9 +643,13 @@ impl PlatformInputHandler {
}
#[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
- fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
+ fn text_for_range(
+ &mut self,
+ range_utf16: Range<usize>,
+ adjusted: &mut Option<Range<usize>>,
+ ) -> Option<String> {
self.cx
- .update(|cx| self.handler.text_for_range(range_utf16, cx))
+ .update(|cx| self.handler.text_for_range(range_utf16, adjusted, cx))
.ok()
.flatten()
}
@@ -712,6 +716,7 @@ impl PlatformInputHandler {
/// A struct representing a selection in a text buffer, in UTF16 characters.
/// This is different from a range because the head may be before the tail.
+#[derive(Debug)]
pub struct UTF16Selection {
/// The range of text in the document this selection corresponds to
/// in UTF16 characters.
@@ -749,6 +754,7 @@ pub trait InputHandler: 'static {
fn text_for_range(
&mut self,
range_utf16: Range<usize>,
+ adjusted_range: &mut Option<Range<usize>>,
cx: &mut WindowContext,
) -> Option<String>;
@@ -38,6 +38,7 @@ use std::{
cell::Cell,
ffi::{c_void, CStr},
mem,
+ ops::Range,
path::PathBuf,
ptr::{self, NonNull},
rc::Rc,
@@ -1754,15 +1755,21 @@ extern "C" fn attributed_substring_for_proposed_range(
this: &Object,
_: Sel,
range: NSRange,
- _actual_range: *mut c_void,
+ actual_range: *mut c_void,
) -> id {
with_input_handler(this, |input_handler| {
let range = range.to_range()?;
if range.is_empty() {
return None;
}
+ let mut adjusted: Option<Range<usize>> = None;
- let selected_text = input_handler.text_for_range(range.clone())?;
+ let selected_text = input_handler.text_for_range(range.clone(), &mut adjusted)?;
+ if let Some(adjusted) = adjusted {
+ if adjusted != range {
+ unsafe { (actual_range as *mut NSRange).write(NSRange::from(adjusted)) };
+ }
+ }
unsafe {
let string: id = msg_send![class!(NSAttributedString), alloc];
let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
@@ -1001,6 +1001,7 @@ impl InputHandler for TerminalInputHandler {
fn text_for_range(
&mut self,
_: std::ops::Range<usize>,
+ _: &mut Option<std::ops::Range<usize>>,
_: &mut WindowContext,
) -> Option<String> {
None