use super::*;
impl Editor {
pub(super) fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
self.selection = Selection::all(&self.doc);
self.history.interrupt();
self.caret_moved();
cx.notify();
}
pub(super) fn selected_source(&self) -> Option<String> {
if self.selection.is_collapsed() {
return None;
}
if self.in_fence() {
let (start, end) = self.selection.clamp(&self.doc).ordered();
let code = self.doc.blocks[start.block].text_at(Part::Code)?;
return Some(code.text[start.offset..end.offset].to_string());
}
Some({
let mut slice = self.doc.slice(self.selection);
slice.normalize_with(&self.marks);
markdown::serialize_with(&slice, &self.marks)
})
}
pub(super) fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
if let Some(source) = self.selected_source() {
cx.write_to_clipboard(gpui::ClipboardItem::new_string(source));
}
}
pub(super) fn cut(&mut self, _: &Cut, _: &mut Window, cx: &mut Context<Self>) {
let Some(source) = self.selected_source() else {
return;
};
cx.write_to_clipboard(gpui::ClipboardItem::new_string(source));
self.edit(EditKind::Structure, cx, |this| {
let splice = this.doc.replace(this.selection, Text::default());
this.selection = Selection::at(splice.caret.clamp(&this.doc));
vec![Delta::Spliced(splice)]
});
}
pub(super) fn paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) {
let Some(item) = cx.read_from_clipboard() else {
return;
};
if let Some(handler) = cx
.try_global::<crate::paste::Installed>()
.map(|held| held.0)
&& let Some(content) = handler(
&item,
&cx.entity(),
crate::PasteContext {
mode: self.mode,
in_fence: self.in_fence(),
base: self.base(),
},
cx,
)
{
match content {
crate::PasteContent::Literal(text) => self.paste_literal(&text, cx),
crate::PasteContent::Markdown(source) => self.paste_markdown(&source, cx),
}
return;
}
if self.in_fence() {
if let Some(text) = item.text() {
self.paste_literal(&text, cx);
}
return;
}
for entry in item.entries() {
let placed = match entry {
gpui::ClipboardEntry::Image(image) => self.paste_image(image, cx),
gpui::ClipboardEntry::ExternalPaths(paths) => self.paste_paths(paths, cx),
gpui::ClipboardEntry::String(_) => false,
};
if placed {
return;
}
}
let Some(source) = item.text() else {
return;
};
self.paste_markdown(&source, cx);
}
fn paste_markdown(&mut self, source: &str, cx: &mut Context<Self>) {
if self.in_fence() {
return self.paste_literal(source, cx);
}
let url = source.trim();
if markdown::is_link(url) {
return self.paste_url(url.to_string(), cx);
}
self.edit(EditKind::Structure, cx, |this| {
let removed = this.selection;
let before = this.doc.blocks.len();
let head = this
.doc
.splice(removed, markdown::parse_with(source, &this.marks));
this.selection = Selection::at(head.clamp(&this.doc));
vec![Delta::Spliced(Splice {
removed,
caret: head,
blocks: this.doc.blocks.len() as isize - before as isize,
})]
});
}
pub(super) fn in_fence(&self) -> bool {
let (start, end) = self.selection.ordered();
start.part == Part::Code && end.part == Part::Code && start.block == end.block
}
pub(super) fn paste_literal(&mut self, text: &str, cx: &mut Context<Self>) {
self.edit(EditKind::Structure, cx, |this| {
let splice = this.doc.replace(this.selection, Text::plain(text));
this.selection = Selection::at(splice.caret.clamp(&this.doc));
vec![Delta::Spliced(splice)]
});
}
pub(super) fn paste_url(&mut self, url: String, cx: &mut Context<Self>) {
if self.in_fence() {
return self.paste_literal(&url, cx);
}
if !self.selection.is_collapsed() {
return self.toggle_mark(Mark::Link(url), cx);
}
let at = self.cursor();
let alone = at.part == Part::Body && self.caret_text().is_some_and(Text::is_empty);
self.edit(EditKind::Structure, cx, |this| {
let splice = this.doc.replace(this.selection, Text::link(&url));
this.selection = Selection::at(splice.caret.clamp(&this.doc));
vec![Delta::Spliced(splice)]
});
if self.chrome.paste && !matches!(at.part, Part::Code | Part::Caption) {
self.pasted = Some(link::Paste::open(at, url, alone));
cx.notify();
}
}
pub(super) fn confirm_paste(&mut self, choice: Choice, cx: &mut Context<Self>) {
let Some(pasted) = self.pasted.take() else {
return;
};
let ix = pasted.at.block;
let card = |url, form| BlockKind::Bookmark { url, form };
match choice {
Choice::Dismiss => cx.notify(),
Choice::Chip => self.edit(EditKind::Structure, cx, |this| {
let end = Cursor {
offset: pasted.at.offset + pasted.url.len(),
..pasted.at
};
let text = Text {
text: pasted.url.clone(),
marks: vec![markdown::MarkSpan {
range: 0..pasted.url.len(),
mark: Mark::Mention {
url: pasted.url,
form: markdown::Form::Chip,
},
}],
};
let splice = this.doc.replace(Selection::new(pasted.at, end), text);
this.selection = Selection::at(splice.caret.clamp(&this.doc));
vec![Delta::Spliced(splice)]
}),
Choice::Bookmark => self.turn_into(ix, card(pasted.url, Form::Auto), cx),
Choice::Embed => self.turn_into(ix, card(pasted.url, Form::Embed(None)), cx),
Choice::Image => self.turn_into(
ix,
BlockKind::Image {
url: pasted.url,
alt: Text::default(),
width: None,
},
cx,
),
}
}
}