Copy/paste images into editors (Mac only) (#15782)

For future reference: WIP branch of copy/pasting a mixture of images and
text: https://github.com/zed-industries/zed/tree/copy-paste-images -
we'll come back to that one after landing this one.

Release Notes:

- You can now paste images into the Assistant Panel to include them as
context. Currently works only on Mac, and with Anthropic models. Future
support is planned for more models, operating systems, and image
clipboard operations.

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Jason <jason@zed.dev>
Co-authored-by: Kyle <kylek@zed.dev>
This commit is contained in:
Richard Feldman
2024-08-13 13:18:25 -04:00
committed by GitHub
co-authored by Antonio Mikayla Jason Kyle
parent e3b0de5dda
commit b1a581e81b
58 changed files with 2983 additions and 1708 deletions
@@ -0,0 +1,122 @@
use cocoa::base::id;
use cocoa::foundation::NSRange;
use objc::{class, msg_send, sel, sel_impl};
/// The `cocoa` crate does not define NSAttributedString (and related Cocoa classes),
/// which are needed for copying rich text (that is, text intermingled with images)
/// to the clipboard. This adds access to those APIs.
#[allow(non_snake_case)]
pub trait NSAttributedString: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSAttributedString), alloc]
}
unsafe fn init_attributed_string(self, string: id) -> id;
unsafe fn appendAttributedString_(self, attr_string: id);
unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
unsafe fn string(self) -> id;
}
impl NSAttributedString for id {
unsafe fn init_attributed_string(self, string: id) -> id {
msg_send![self, initWithString: string]
}
unsafe fn appendAttributedString_(self, attr_string: id) {
let _: () = msg_send![self, appendAttributedString: attr_string];
}
unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
msg_send![self, RTFDFromRange: range documentAttributes: attrs]
}
unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
msg_send![self, RTFFromRange: range documentAttributes: attrs]
}
unsafe fn string(self) -> id {
msg_send![self, string]
}
}
pub trait NSMutableAttributedString: NSAttributedString {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSMutableAttributedString), alloc]
}
}
impl NSMutableAttributedString for id {}
#[cfg(test)]
mod tests {
use super::*;
use cocoa::appkit::NSImage;
use cocoa::base::nil;
use cocoa::foundation::NSString;
#[test]
#[ignore] // This was SIGSEGV-ing on CI but not locally; need to investigate https://github.com/zed-industries/zed/actions/runs/10362363230/job/28684225486?pr=15782#step:4:1348
fn test_nsattributed_string() {
// TODO move these to parent module once it's actually ready to be used
#[allow(non_snake_case)]
pub trait NSTextAttachment: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSTextAttachment), alloc]
}
}
impl NSTextAttachment for id {}
unsafe {
let image: id = msg_send![class!(NSImage), alloc];
image.initWithContentsOfFile_(
NSString::alloc(nil).init_str("/Users/rtfeldman/Downloads/test.jpeg"),
);
let _size = image.size();
let string = NSString::alloc(nil).init_str("Test String");
let attr_string = NSMutableAttributedString::alloc(nil).init_attributed_string(string);
let hello_string = NSString::alloc(nil).init_str("Hello World");
let hello_attr_string =
NSAttributedString::alloc(nil).init_attributed_string(hello_string);
attr_string.appendAttributedString_(hello_attr_string);
let attachment = NSTextAttachment::alloc(nil);
let _: () = msg_send![attachment, setImage: image];
let image_attr_string =
msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment];
attr_string.appendAttributedString_(image_attr_string);
let another_string = NSString::alloc(nil).init_str("Another String");
let another_attr_string =
NSAttributedString::alloc(nil).init_attributed_string(another_string);
attr_string.appendAttributedString_(another_attr_string);
let _len: cocoa::foundation::NSUInteger = msg_send![attr_string, length];
///////////////////////////////////////////////////
// pasteboard.clearContents();
let rtfd_data = attr_string.RTFDFromRange_documentAttributes_(
NSRange::new(0, msg_send![attr_string, length]),
nil,
);
assert_ne!(rtfd_data, nil);
// if rtfd_data != nil {
// pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD);
// }
// let rtf_data = attributed_string.RTFFromRange_documentAttributes_(
// NSRange::new(0, attributed_string.length()),
// nil,
// );
// if rtf_data != nil {
// pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF);
// }
// let plain_text = attributed_string.string();
// pasteboard.setString_forType(plain_text, NSPasteboardTypeString);
}
}
}