//! 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, pub file_system_edits: Vec, 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, file_system_edits: Vec, ) -> 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>(&mut self, iter: T) { iter.into_iter().for_each(|(file_id, edit)| self.insert_source_edit(file_id, edit)); } } impl Extend for SourceChange { fn extend>(&mut self, iter: T) { iter.into_iter().for_each(|edit| self.push_file_system_edit(edit)); } } impl From> for SourceChange { fn from(source_file_edits: FxHashMap) -> 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 }, } impl From for SourceChange { fn from(edit: FileSystemEdit) -> SourceChange { SourceChange { source_file_edits: Default::default(), file_system_edits: vec![edit], is_snippet: false, } } }