utils.rs

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