utils.rs

  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}