diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-db/src/source_change.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs new file mode 100644 index 000000000..8132c73ef --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -0,0 +1,99 @@ +//! This modules defines type to represent changes to the source code, that flow +//! from the server to the client. +//! +//! It can be viewed as a dual for `Change`. + +use std::{collections::hash_map::Entry, iter}; + +use base_db::{AnchoredPathBuf, FileId}; +use rustc_hash::FxHashMap; +use stdx::never; +use text_edit::TextEdit; + +#[derive(Default, Debug, Clone)] +pub struct SourceChange { + pub source_file_edits: FxHashMap<FileId, TextEdit>, + pub file_system_edits: Vec<FileSystemEdit>, + pub is_snippet: bool, +} + +impl SourceChange { + /// Creates a new SourceChange with the given label + /// from the edits. + pub fn from_edits( + source_file_edits: FxHashMap<FileId, TextEdit>, + file_system_edits: Vec<FileSystemEdit>, + ) -> Self { + SourceChange { source_file_edits, file_system_edits, is_snippet: false } + } + + pub fn from_text_edit(file_id: FileId, edit: TextEdit) -> Self { + SourceChange { + source_file_edits: iter::once((file_id, edit)).collect(), + ..Default::default() + } + } + + /// Inserts a [`TextEdit`] for the given [`FileId`]. This properly handles merging existing + /// edits for a file if some already exist. + pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) { + match self.source_file_edits.entry(file_id) { + Entry::Occupied(mut entry) => { + never!(entry.get_mut().union(edit).is_err(), "overlapping edits for same file"); + } + Entry::Vacant(entry) => { + entry.insert(edit); + } + } + } + + pub fn push_file_system_edit(&mut self, edit: FileSystemEdit) { + self.file_system_edits.push(edit); + } + + pub fn get_source_edit(&self, file_id: FileId) -> Option<&TextEdit> { + self.source_file_edits.get(&file_id) + } + + pub fn merge(mut self, other: SourceChange) -> SourceChange { + self.extend(other.source_file_edits); + self.extend(other.file_system_edits); + self.is_snippet |= other.is_snippet; + self + } +} + +impl Extend<(FileId, TextEdit)> for SourceChange { + fn extend<T: IntoIterator<Item = (FileId, TextEdit)>>(&mut self, iter: T) { + iter.into_iter().for_each(|(file_id, edit)| self.insert_source_edit(file_id, edit)); + } +} + +impl Extend<FileSystemEdit> for SourceChange { + fn extend<T: IntoIterator<Item = FileSystemEdit>>(&mut self, iter: T) { + iter.into_iter().for_each(|edit| self.push_file_system_edit(edit)); + } +} + +impl From<FxHashMap<FileId, TextEdit>> for SourceChange { + fn from(source_file_edits: FxHashMap<FileId, TextEdit>) -> SourceChange { + SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } + } +} + +#[derive(Debug, Clone)] +pub enum FileSystemEdit { + CreateFile { dst: AnchoredPathBuf, initial_contents: String }, + MoveFile { src: FileId, dst: AnchoredPathBuf }, + MoveDir { src: AnchoredPathBuf, src_id: FileId, dst: AnchoredPathBuf }, +} + +impl From<FileSystemEdit> for SourceChange { + fn from(edit: FileSystemEdit) -> SourceChange { + SourceChange { + source_file_edits: Default::default(), + file_system_edits: vec![edit], + is_snippet: false, + } + } +} |