1use std::time::Duration;
2
3use editor::{ClipboardSelection, Editor};
4use gpui::{ClipboardItem, ViewContext};
5use language::{CharKind, Point};
6
7pub struct HighlightOnYank;
8
9pub fn copy_and_flash_selections_content(
10 editor: &mut Editor,
11 linewise: bool,
12 cx: &mut ViewContext<Editor>,
13) {
14 copy_selections_content_internal(editor, linewise, true, cx);
15}
16
17pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut ViewContext<Editor>) {
18 copy_selections_content_internal(editor, linewise, false, cx);
19}
20
21fn copy_selections_content_internal(
22 editor: &mut Editor,
23 linewise: bool,
24 highlight: bool,
25 cx: &mut ViewContext<Editor>,
26) {
27 let selections = editor.selections.all_adjusted(cx);
28 let buffer = editor.buffer().read(cx).snapshot(cx);
29 let mut text = String::new();
30 let mut clipboard_selections = Vec::with_capacity(selections.len());
31 let mut ranges_to_highlight = Vec::new();
32 {
33 let mut is_first = true;
34 for selection in selections.iter() {
35 let mut start = selection.start;
36 let end = selection.end;
37 if is_first {
38 is_first = false;
39 } else {
40 text.push_str("\n");
41 }
42 let initial_len = text.len();
43
44 // if the file does not end with \n, and our line-mode selection ends on
45 // that line, we will have expanded the start of the selection to ensure it
46 // contains a newline (so that delete works as expected). We undo that change
47 // here.
48 let is_last_line = linewise
49 && end.row == buffer.max_buffer_row()
50 && buffer.max_point().column > 0
51 && start.row < buffer.max_buffer_row()
52 && start == Point::new(start.row, buffer.line_len(start.row));
53
54 if is_last_line {
55 start = Point::new(start.row + 1, 0);
56 }
57
58 let start_anchor = buffer.anchor_after(start);
59 let end_anchor = buffer.anchor_before(end);
60 ranges_to_highlight.push(start_anchor..end_anchor);
61
62 for chunk in buffer.text_for_range(start..end) {
63 text.push_str(chunk);
64 }
65 if is_last_line {
66 text.push_str("\n");
67 }
68 clipboard_selections.push(ClipboardSelection {
69 len: text.len() - initial_len,
70 is_entire_line: linewise,
71 first_line_indent: buffer.indent_size_for_line(start.row).len,
72 });
73 }
74 }
75
76 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
77 if !highlight {
78 return;
79 }
80
81 editor.highlight_background::<HighlightOnYank>(
82 ranges_to_highlight,
83 |colors| colors.editor_document_highlight_read_background,
84 cx,
85 );
86 cx.spawn(|this, mut cx| async move {
87 cx.background_executor()
88 .timer(Duration::from_millis(200))
89 .await;
90 this.update(&mut cx, |editor, cx| {
91 editor.clear_background_highlights::<HighlightOnYank>(cx)
92 })
93 .ok();
94 })
95 .detach();
96}
97
98pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
99 if treat_punctuation_as_word && kind == CharKind::Punctuation {
100 CharKind::Word
101 } else {
102 kind
103 }
104}