From f4c1ffc3294ee1743d68e76d41c5ca0624edc4fe Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 13 Apr 2021 14:58:10 +0200 Subject: [PATCH] Start on copy-paste --- gpui/src/app.rs | 4 +++ gpui/src/platform/mac/platform.rs | 14 ++++++++ gpui/src/platform/mod.rs | 1 + gpui/src/platform/test.rs | 4 +++ zed/src/editor/buffer/mod.rs | 15 ++++---- zed/src/editor/buffer_view.rs | 58 ++++++++++++++++++++++++++++++- 6 files changed, 87 insertions(+), 9 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 4f5fb7b905..61c7ce6bc9 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -1215,6 +1215,10 @@ impl MutableAppContext { pub fn copy(&self, text: &str) { self.platform.copy(text); } + + pub fn paste(&self) -> Option { + self.platform.paste() + } } impl ReadModel for MutableAppContext { diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 4fc0f160a3..570d3ea14c 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -26,6 +26,7 @@ use std::{ path::PathBuf, ptr, rc::Rc, + slice, sync::Arc, }; @@ -299,6 +300,19 @@ impl platform::Platform for MacPlatform { } } + fn paste(&self) -> Option { + unsafe { + let pasteboard = NSPasteboard::generalPasteboard(nil); + let data = pasteboard.dataForType(NSPasteboardTypeString); + if data == nil { + None + } else { + let bytes = slice::from_raw_parts(data.bytes() as *mut u8, data.length() as usize); + Some(String::from_utf8_unchecked(bytes.to_vec())) + } + } + } + fn set_menus(&self, menus: Vec) { unsafe { let app: id = msg_send![APP_CLASS, sharedApplication]; diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 52148ee27c..39439970d8 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -43,6 +43,7 @@ pub trait Platform { fn prompt_for_paths(&self, options: PathPromptOptions) -> Option>; fn quit(&self); fn copy(&self, text: &str); + fn paste(&self) -> Option; fn set_menus(&self, menus: Vec); } diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index eed3709cba..19c015f688 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -73,6 +73,10 @@ impl super::Platform for Platform { } fn copy(&self, _: &str) {} + + fn paste(&self) -> Option { + None + } } impl Window { diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 7f06e1700f..3348199ad7 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -563,10 +563,13 @@ impl Buffer { self.chars().collect() } - pub fn text_for_range(&self, range: Range) -> Result { + pub fn text_for_range<'a, T: ToOffset>( + &'a self, + range: Range, + ) -> Result> { let start = range.start.to_offset(self)?; let end = range.end.to_offset(self)?; - Ok(self.chars_at(start)?.take(end - start).collect()) + Ok(self.chars_at(start)?.take(end - start)) } pub fn chars(&self) -> CharIter { @@ -2470,13 +2473,9 @@ mod tests { let old_len = old_range.end - old_range.start; let new_len = new_range.end - new_range.start; let old_start = (old_range.start as isize + delta) as usize; - + let new_text: String = buffer.text_for_range(new_range).unwrap().collect(); old_buffer - .edit( - Some(old_start..old_start + old_len), - buffer.text_for_range(new_range).unwrap(), - None, - ) + .edit(Some(old_start..old_start + old_len), new_text, None) .unwrap(); delta += new_len as isize - old_len as isize; diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index aa91e39ec5..b90b79fa12 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1,6 +1,6 @@ use super::{ buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point, - Selection, SelectionSetId, ToOffset, + Selection, SelectionSetId, ToOffset, ToPoint, }; use crate::{settings::Settings, watch, workspace}; use anyhow::Result; @@ -28,6 +28,9 @@ pub fn init(app: &mut MutableAppContext) { app.add_bindings(vec![ Binding::new("backspace", "buffer:backspace", Some("BufferView")), Binding::new("enter", "buffer:newline", Some("BufferView")), + Binding::new("cmd-x", "buffer:cut", Some("BufferView")), + Binding::new("cmd-c", "buffer:copy", Some("BufferView")), + Binding::new("cmd-v", "buffer:paste", Some("BufferView")), Binding::new("cmd-z", "buffer:undo", Some("BufferView")), Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")), Binding::new("up", "buffer:move_up", Some("BufferView")), @@ -54,6 +57,9 @@ pub fn init(app: &mut MutableAppContext) { app.add_action("buffer:insert", BufferView::insert); app.add_action("buffer:newline", BufferView::newline); app.add_action("buffer:backspace", BufferView::backspace); + app.add_action("buffer:cut", BufferView::cut); + app.add_action("buffer:copy", BufferView::copy); + app.add_action("buffer:paste", BufferView::paste); app.add_action("buffer:undo", BufferView::undo); app.add_action("buffer:redo", BufferView::redo); app.add_action("buffer:move_up", BufferView::move_up); @@ -449,6 +455,56 @@ impl BufferView { self.end_transaction(ctx); } + pub fn cut(&mut self, _: &(), ctx: &mut ViewContext) { + self.start_transaction(ctx); + let mut text = String::new(); + let mut selections = self.selections(ctx.app()).to_vec(); + { + let buffer = self.buffer.read(ctx); + let max_point = buffer.max_point(); + for selection in &mut selections { + let mut start = selection.start.to_point(buffer).expect("invalid start"); + let mut end = selection.end.to_point(buffer).expect("invalid end"); + if start == end { + start = Point::new(start.row, 0); + end = cmp::min(max_point, Point::new(start.row + 1, 0)); + selection.start = buffer.anchor_before(start).unwrap(); + selection.end = buffer.anchor_after(end).unwrap(); + } + text.extend(buffer.text_for_range(start..end).unwrap()); + } + } + self.update_selections(selections, ctx); + self.changed_selections(ctx); + self.insert(&String::new(), ctx); + self.end_transaction(ctx); + + ctx.app_mut().copy(&text); + } + + pub fn copy(&mut self, _: &(), ctx: &mut ViewContext) { + let buffer = self.buffer.read(ctx); + let max_point = buffer.max_point(); + let mut text = String::new(); + for selection in self.selections(ctx.app()) { + let mut start = selection.start.to_point(buffer).expect("invalid start"); + let mut end = selection.end.to_point(buffer).expect("invalid end"); + if start == end { + start = Point::new(start.row, 0); + end = cmp::min(max_point, Point::new(start.row + 1, 0)); + } + text.extend(buffer.text_for_range(start..end).unwrap()); + } + + ctx.app_mut().copy(&text); + } + + pub fn paste(&mut self, _: &(), ctx: &mut ViewContext) { + if let Some(text) = ctx.app_mut().paste() { + self.insert(&text, ctx); + } + } + pub fn undo(&mut self, _: &(), ctx: &mut ViewContext) { self.buffer .update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));