Skip to main content

perl_parser/incremental/
edit.rs

1use crate::incremental::LineIndex;
2
3/// Edit description
4#[derive(Clone, Debug)]
5pub struct Edit {
6    pub start_byte: usize,
7    pub old_end_byte: usize,
8    pub new_end_byte: usize,
9    pub new_text: String,
10}
11
12impl Edit {
13    /// Returns the size of text touched by this edit (inserted or deleted).
14    pub(crate) fn touched_bytes(&self) -> usize {
15        let replaced_len = self.old_end_byte.saturating_sub(self.start_byte);
16        replaced_len.max(self.new_text.len())
17    }
18}
19
20#[cfg(feature = "lsp-compat")]
21impl Edit {
22    /// Convert LSP change to Edit
23    pub fn from_lsp_change(
24        change: &lsp_types::TextDocumentContentChangeEvent,
25        line_index: &LineIndex,
26        old_text: &str,
27    ) -> Option<Self> {
28        if let Some(range) = change.range {
29            // `range.start.character` / `range.end.character` are UTF-16 code unit
30            // offsets as specified by the LSP protocol.  Use the UTF-16-aware
31            // conversion so that lines containing multibyte characters (e.g.
32            // `my $café = 1;`) map to the correct byte offset rather than treating
33            // the UTF-16 column as a raw byte count (fixes #750).
34            let start_byte = line_index.position_to_byte_utf16(
35                old_text,
36                range.start.line as usize,
37                range.start.character as usize,
38            )?;
39            let old_end_byte = line_index.position_to_byte_utf16(
40                old_text,
41                range.end.line as usize,
42                range.end.character as usize,
43            )?;
44            let new_end_byte = start_byte + change.text.len();
45
46            Some(Edit { start_byte, old_end_byte, new_end_byte, new_text: change.text.clone() })
47        } else {
48            Some(Edit {
49                start_byte: 0,
50                old_end_byte: old_text.len(),
51                new_end_byte: change.text.len(),
52                new_text: change.text.clone(),
53            })
54        }
55    }
56}