From 6dafc13f5f776980cd2560fb79d3d4790811c96d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Aug 2020 17:03:06 +0200 Subject: Rename ra_text_edit -> text_edit --- crates/ra_assists/Cargo.toml | 2 +- crates/ra_assists/src/assist_context.rs | 2 +- crates/ra_assists/src/utils/insert_use.rs | 4 +- crates/ra_ide/Cargo.toml | 2 +- crates/ra_ide/src/completion/complete_postfix.rs | 2 +- .../ra_ide/src/completion/complete_trait_impl.rs | 2 +- crates/ra_ide/src/completion/completion_context.rs | 2 +- crates/ra_ide/src/completion/completion_item.rs | 2 +- crates/ra_ide/src/diagnostics.rs | 2 +- .../ra_ide/src/diagnostics/diagnostics_with_fix.rs | 2 +- crates/ra_ide/src/join_lines.rs | 2 +- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide/src/references/rename.rs | 4 +- crates/ra_ide/src/typing.rs | 2 +- crates/ra_ide/src/typing/on_enter.rs | 2 +- crates/ra_ide_db/Cargo.toml | 2 +- crates/ra_ide_db/src/source_change.rs | 2 +- crates/ra_ssr/Cargo.toml | 2 +- crates/ra_ssr/src/replacing.rs | 2 +- crates/ra_syntax/Cargo.toml | 2 +- crates/ra_syntax/fuzz/Cargo.toml | 2 +- crates/ra_syntax/src/algo.rs | 2 +- crates/ra_syntax/src/fuzz.rs | 2 +- crates/ra_syntax/src/lib.rs | 2 +- crates/ra_syntax/src/parsing/reparsing.rs | 2 +- crates/ra_text_edit/Cargo.toml | 12 -- crates/ra_text_edit/src/lib.rs | 186 --------------------- crates/rust-analyzer/Cargo.toml | 2 +- crates/text_edit/Cargo.toml | 12 ++ crates/text_edit/src/lib.rs | 186 +++++++++++++++++++++ 30 files changed, 226 insertions(+), 226 deletions(-) delete mode 100644 crates/ra_text_edit/Cargo.toml delete mode 100644 crates/ra_text_edit/src/lib.rs create mode 100644 crates/text_edit/Cargo.toml create mode 100644 crates/text_edit/src/lib.rs (limited to 'crates') diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 6f5ace941..e4a5ee6c1 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml @@ -16,7 +16,7 @@ either = "1.5.3" stdx = { path = "../stdx" } ra_syntax = { path = "../ra_syntax" } -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } ra_fmt = { path = "../ra_fmt" } profile = { path = "../profile" } ra_db = { path = "../ra_db" } diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index afba860d1..fcaa1aedc 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -15,7 +15,7 @@ use ra_syntax::{ AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, }; -use ra_text_edit::{TextEdit, TextEditBuilder}; +use text_edit::{TextEdit, TextEditBuilder}; use crate::{ assist_config::{AssistConfig, SnippetCap}, diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 32780fceb..13dbe1919 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -2,6 +2,7 @@ // FIXME: rewrite according to the plan, outlined in // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 +use either::Either; use hir::{self, ModPath}; use ra_syntax::{ ast::{self, NameOwner, VisibilityOwner}, @@ -9,10 +10,9 @@ use ra_syntax::{ SyntaxKind::{PATH, PATH_SEGMENT}, SyntaxNode, T, }; -use ra_text_edit::TextEditBuilder; +use text_edit::TextEditBuilder; use crate::assist_context::AssistContext; -use either::Either; /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. pub(crate) fn find_insert_use_container( diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index bbc9ba4e7..84c25f0b8 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -22,7 +22,7 @@ oorandom = "11.1.2" stdx = { path = "../stdx" } ra_syntax = { path = "../ra_syntax" } -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } ra_db = { path = "../ra_db" } ra_ide_db = { path = "../ra_ide_db" } ra_cfg = { path = "../ra_cfg" } diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 8735b9010..42087da8d 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs @@ -4,7 +4,7 @@ use ra_syntax::{ ast::{self, AstNode}, TextRange, TextSize, }; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; use crate::{ completion::{ diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index d9a0ef167..b397baf10 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -37,7 +37,7 @@ use ra_syntax::{ ast::{self, edit, Impl}, AstNode, SyntaxKind, SyntaxNode, TextRange, T, }; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; use crate::{ completion::{ diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 4aa761148..0cb57fb1b 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -9,7 +9,7 @@ use ra_syntax::{ SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, }; -use ra_text_edit::Indel; +use text_edit::Indel; use super::patterns::{ has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent, diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 7bdda316c..1c0684f4e 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -4,7 +4,7 @@ use std::fmt; use hir::Documentation; use ra_syntax::TextRange; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; use crate::completion::completion_config::SnippetCap; diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index e006c7775..54810d5bb 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -14,7 +14,7 @@ use ra_syntax::{ ast::{self, AstNode}, SyntaxNode, TextRange, T, }; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; use crate::{Diagnostic, FileId, Fix, SourceFileEdit}; diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs index 88e593e00..8fb25de6c 100644 --- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs +++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs @@ -13,7 +13,7 @@ use ra_ide_db::{ RootDatabase, }; use ra_syntax::{algo, ast, AstNode}; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; /// A [Diagnostic] that potentially has a fix available. /// diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index 1c881386f..caf63933a 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs @@ -7,7 +7,7 @@ use ra_syntax::{ SyntaxKind::{self, WHITESPACE}, SyntaxNode, SyntaxToken, TextRange, TextSize, T, }; -use ra_text_edit::{TextEdit, TextEditBuilder}; +use text_edit::{TextEdit, TextEditBuilder}; // Feature: Join Lines // diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index bfcf5d750..09cb5faf6 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -96,7 +96,7 @@ pub use ra_ide_db::{ RootDatabase, }; pub use ra_ssr::SsrError; -pub use ra_text_edit::{Indel, TextEdit}; +pub use text_edit::{Indel, TextEdit}; pub type Cancelable = Result; diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 8c1ac3c56..9c688fb06 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -11,9 +11,9 @@ use ra_syntax::{ ast::{self, NameOwner}, lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, }; -use ra_text_edit::TextEdit; use std::convert::TryInto; use test_utils::mark; +use text_edit::TextEdit; use crate::{ references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, @@ -271,9 +271,9 @@ fn rename_reference( #[cfg(test)] mod tests { use expect::{expect, Expect}; - use ra_text_edit::TextEditBuilder; use stdx::trim_indent; use test_utils::{assert_eq_text, mark}; + use text_edit::TextEdit; use crate::{mock_analysis::analysis_and_position, FileId}; diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index d3ce744b4..952429cde 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -26,7 +26,7 @@ use ra_syntax::{ TextRange, TextSize, }; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; use crate::SourceChange; diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 143b1ae41..c0c5ce3bc 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs @@ -9,8 +9,8 @@ use ra_syntax::{ SyntaxKind::*, SyntaxToken, TextRange, TextSize, TokenAtOffset, }; -use ra_text_edit::TextEdit; use test_utils::mark; +use text_edit::TextEdit; // Feature: On Enter // diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 92b8ef82a..5446a5961 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml @@ -22,7 +22,7 @@ either = "1.5.3" stdx = { path = "../stdx" } ra_syntax = { path = "../ra_syntax" } -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } ra_db = { path = "../ra_db" } profile = { path = "../profile" } test_utils = { path = "../test_utils" } diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index abb83f421..ae21132dd 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs @@ -4,7 +4,7 @@ //! It can be viewed as a dual for `AnalysisChange`. use ra_db::FileId; -use ra_text_edit::TextEdit; +use text_edit::TextEdit; #[derive(Default, Debug, Clone)] pub struct SourceChange { diff --git a/crates/ra_ssr/Cargo.toml b/crates/ra_ssr/Cargo.toml index 84e4b171e..d0f2ae733 100644 --- a/crates/ra_ssr/Cargo.toml +++ b/crates/ra_ssr/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer" doctest = false [dependencies] -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } ra_syntax = { path = "../ra_syntax" } ra_db = { path = "../ra_db" } ra_ide_db = { path = "../ra_ide_db" } diff --git a/crates/ra_ssr/src/replacing.rs b/crates/ra_ssr/src/replacing.rs index 36ced3842..74f9e7db6 100644 --- a/crates/ra_ssr/src/replacing.rs +++ b/crates/ra_ssr/src/replacing.rs @@ -4,8 +4,8 @@ use crate::matching::Var; use crate::{resolving::ResolvedRule, Match, SsrMatches}; use ra_syntax::ast::{self, AstToken}; use ra_syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; -use ra_text_edit::TextEdit; use rustc_hash::{FxHashMap, FxHashSet}; +use text_edit::TextEdit; /// Returns a text edit that will replace each match in `matches` with its corresponding replacement /// template. Placeholders in the template will have been substituted with whatever they matched to diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index fc4d7aa04..f2789e6a3 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml @@ -20,7 +20,7 @@ once_cell = "1.3.1" stdx = { path = "../stdx" } -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } ra_parser = { path = "../ra_parser" } # This crate transitively depends on `smol_str` via `rowan`. diff --git a/crates/ra_syntax/fuzz/Cargo.toml b/crates/ra_syntax/fuzz/Cargo.toml index 613ad2857..4cec3c4cd 100644 --- a/crates/ra_syntax/fuzz/Cargo.toml +++ b/crates/ra_syntax/fuzz/Cargo.toml @@ -11,7 +11,7 @@ cargo-fuzz = true [dependencies] ra_syntax = { path = ".." } -ra_text_edit = { path = "../../ra_text_edit" } +text_edit = { path = "../../text_edit" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } # Prevent this from interfering with workspaces diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 26b3c813a..6254b38ba 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs @@ -6,8 +6,8 @@ use std::{ }; use itertools::Itertools; -use ra_text_edit::TextEditBuilder; use rustc_hash::FxHashMap; +use text_edit::TextEditBuilder; use crate::{ AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs index 39f9b12ab..fbb97aa27 100644 --- a/crates/ra_syntax/src/fuzz.rs +++ b/crates/ra_syntax/src/fuzz.rs @@ -5,7 +5,7 @@ use std::{ str::{self, FromStr}, }; -use ra_text_edit::Indel; +use text_edit::Indel; use crate::{validation, AstNode, SourceFile, TextRange}; diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 8a4d45386..465607f55 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs @@ -39,8 +39,8 @@ pub mod fuzz; use std::{marker::PhantomData, sync::Arc}; -use ra_text_edit::Indel; use stdx::format_to; +use text_edit::Indel; pub use crate::{ algo::InsertPosition, diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs index ed5a42ea3..6644ffca4 100644 --- a/crates/ra_syntax/src/parsing/reparsing.rs +++ b/crates/ra_syntax/src/parsing/reparsing.rs @@ -7,7 +7,7 @@ //! and try to parse only this block. use ra_parser::Reparser; -use ra_text_edit::Indel; +use text_edit::Indel; use crate::{ algo, diff --git a/crates/ra_text_edit/Cargo.toml b/crates/ra_text_edit/Cargo.toml deleted file mode 100644 index 427862a5c..000000000 --- a/crates/ra_text_edit/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "ra_text_edit" -version = "0.0.0" -license = "MIT OR Apache-2.0" -authors = ["rust-analyzer developers"] -edition = "2018" - -[lib] -doctest = false - -[dependencies] -text-size = "1.0.0" diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs deleted file mode 100644 index ab8cd7fd1..000000000 --- a/crates/ra_text_edit/src/lib.rs +++ /dev/null @@ -1,186 +0,0 @@ -//! Representation of a `TextEdit`. -//! -//! `rust-analyzer` never mutates text itself and only sends diffs to clients, -//! so `TextEdit` is the ultimate representation of the work done by -//! rust-analyzer. -pub use text_size::{TextRange, TextSize}; - -/// `InsertDelete` -- a single "atomic" change to text -/// -/// Must not overlap with other `InDel`s -#[derive(Debug, Clone)] -pub struct Indel { - pub insert: String, - /// Refers to offsets in the original text - pub delete: TextRange, -} - -#[derive(Default, Debug, Clone)] -pub struct TextEdit { - indels: Vec, -} - -#[derive(Debug, Default, Clone)] -pub struct TextEditBuilder { - indels: Vec, -} - -impl Indel { - pub fn insert(offset: TextSize, text: String) -> Indel { - Indel::replace(TextRange::empty(offset), text) - } - pub fn delete(range: TextRange) -> Indel { - Indel::replace(range, String::new()) - } - pub fn replace(range: TextRange, replace_with: String) -> Indel { - Indel { delete: range, insert: replace_with } - } - - pub fn apply(&self, text: &mut String) { - let start: usize = self.delete.start().into(); - let end: usize = self.delete.end().into(); - text.replace_range(start..end, &self.insert); - } -} - -impl TextEdit { - pub fn builder() -> TextEditBuilder { - TextEditBuilder::default() - } - - pub fn insert(offset: TextSize, text: String) -> TextEdit { - let mut builder = TextEdit::builder(); - builder.insert(offset, text); - builder.finish() - } - - pub fn delete(range: TextRange) -> TextEdit { - let mut builder = TextEdit::builder(); - builder.delete(range); - builder.finish() - } - - pub fn replace(range: TextRange, replace_with: String) -> TextEdit { - let mut builder = TextEdit::builder(); - builder.replace(range, replace_with); - builder.finish() - } - - pub fn len(&self) -> usize { - self.indels.len() - } - - pub fn is_empty(&self) -> bool { - self.indels.is_empty() - } - - pub fn iter(&self) -> std::slice::Iter<'_, Indel> { - self.into_iter() - } - - pub fn apply(&self, text: &mut String) { - match self.len() { - 0 => return, - 1 => { - self.indels[0].apply(text); - return; - } - _ => (), - } - - let mut total_len = TextSize::of(&*text); - for indel in self.indels.iter() { - total_len += TextSize::of(&indel.insert); - total_len -= indel.delete.end() - indel.delete.start(); - } - let mut buf = String::with_capacity(total_len.into()); - let mut prev = 0; - for indel in self.indels.iter() { - let start: usize = indel.delete.start().into(); - let end: usize = indel.delete.end().into(); - if start > prev { - buf.push_str(&text[prev..start]); - } - buf.push_str(&indel.insert); - prev = end; - } - buf.push_str(&text[prev..text.len()]); - assert_eq!(TextSize::of(&buf), total_len); - - // FIXME: figure out a way to mutate the text in-place or reuse the - // memory in some other way - *text = buf - } - - pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> { - // FIXME: can be done without allocating intermediate vector - let mut all = self.iter().chain(other.iter()).collect::>(); - if !check_disjoint(&mut all) { - return Err(other); - } - self.indels.extend(other.indels); - assert!(check_disjoint(&mut self.indels)); - Ok(()) - } - - pub fn apply_to_offset(&self, offset: TextSize) -> Option { - let mut res = offset; - for indel in self.indels.iter() { - if indel.delete.start() >= offset { - break; - } - if offset < indel.delete.end() { - return None; - } - res += TextSize::of(&indel.insert); - res -= indel.delete.len(); - } - Some(res) - } -} - -impl IntoIterator for TextEdit { - type Item = Indel; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.indels.into_iter() - } -} - -impl<'a> IntoIterator for &'a TextEdit { - type Item = &'a Indel; - type IntoIter = std::slice::Iter<'a, Indel>; - - fn into_iter(self) -> Self::IntoIter { - self.indels.iter() - } -} - -impl TextEditBuilder { - pub fn replace(&mut self, range: TextRange, replace_with: String) { - self.indels.push(Indel::replace(range, replace_with)) - } - pub fn delete(&mut self, range: TextRange) { - self.indels.push(Indel::delete(range)) - } - pub fn insert(&mut self, offset: TextSize, text: String) { - self.indels.push(Indel::insert(offset, text)) - } - pub fn finish(self) -> TextEdit { - let mut indels = self.indels; - assert!(check_disjoint(&mut indels)); - TextEdit { indels } - } - pub fn invalidates_offset(&self, offset: TextSize) -> bool { - self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) - } -} - -fn check_disjoint(indels: &mut [impl std::borrow::Borrow]) -> bool { - indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end())); - indels - .iter() - .zip(indels.iter().skip(1)) - .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start()) -} diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 3f70510fd..ef244da59 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -39,7 +39,7 @@ ra_ide = { path = "../ra_ide" } profile = { path = "../profile" } ra_project_model = { path = "../ra_project_model" } ra_syntax = { path = "../ra_syntax" } -ra_text_edit = { path = "../ra_text_edit" } +text_edit = { path = "../text_edit" } vfs = { path = "../vfs" } vfs-notify = { path = "../vfs-notify" } ra_cfg = { path = "../ra_cfg" } diff --git a/crates/text_edit/Cargo.toml b/crates/text_edit/Cargo.toml new file mode 100644 index 000000000..a69b1ef2b --- /dev/null +++ b/crates/text_edit/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "text_edit" +version = "0.0.0" +license = "MIT OR Apache-2.0" +authors = ["rust-analyzer developers"] +edition = "2018" + +[lib] +doctest = false + +[dependencies] +text-size = "1.0.0" diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs new file mode 100644 index 000000000..ab8cd7fd1 --- /dev/null +++ b/crates/text_edit/src/lib.rs @@ -0,0 +1,186 @@ +//! Representation of a `TextEdit`. +//! +//! `rust-analyzer` never mutates text itself and only sends diffs to clients, +//! so `TextEdit` is the ultimate representation of the work done by +//! rust-analyzer. +pub use text_size::{TextRange, TextSize}; + +/// `InsertDelete` -- a single "atomic" change to text +/// +/// Must not overlap with other `InDel`s +#[derive(Debug, Clone)] +pub struct Indel { + pub insert: String, + /// Refers to offsets in the original text + pub delete: TextRange, +} + +#[derive(Default, Debug, Clone)] +pub struct TextEdit { + indels: Vec, +} + +#[derive(Debug, Default, Clone)] +pub struct TextEditBuilder { + indels: Vec, +} + +impl Indel { + pub fn insert(offset: TextSize, text: String) -> Indel { + Indel::replace(TextRange::empty(offset), text) + } + pub fn delete(range: TextRange) -> Indel { + Indel::replace(range, String::new()) + } + pub fn replace(range: TextRange, replace_with: String) -> Indel { + Indel { delete: range, insert: replace_with } + } + + pub fn apply(&self, text: &mut String) { + let start: usize = self.delete.start().into(); + let end: usize = self.delete.end().into(); + text.replace_range(start..end, &self.insert); + } +} + +impl TextEdit { + pub fn builder() -> TextEditBuilder { + TextEditBuilder::default() + } + + pub fn insert(offset: TextSize, text: String) -> TextEdit { + let mut builder = TextEdit::builder(); + builder.insert(offset, text); + builder.finish() + } + + pub fn delete(range: TextRange) -> TextEdit { + let mut builder = TextEdit::builder(); + builder.delete(range); + builder.finish() + } + + pub fn replace(range: TextRange, replace_with: String) -> TextEdit { + let mut builder = TextEdit::builder(); + builder.replace(range, replace_with); + builder.finish() + } + + pub fn len(&self) -> usize { + self.indels.len() + } + + pub fn is_empty(&self) -> bool { + self.indels.is_empty() + } + + pub fn iter(&self) -> std::slice::Iter<'_, Indel> { + self.into_iter() + } + + pub fn apply(&self, text: &mut String) { + match self.len() { + 0 => return, + 1 => { + self.indels[0].apply(text); + return; + } + _ => (), + } + + let mut total_len = TextSize::of(&*text); + for indel in self.indels.iter() { + total_len += TextSize::of(&indel.insert); + total_len -= indel.delete.end() - indel.delete.start(); + } + let mut buf = String::with_capacity(total_len.into()); + let mut prev = 0; + for indel in self.indels.iter() { + let start: usize = indel.delete.start().into(); + let end: usize = indel.delete.end().into(); + if start > prev { + buf.push_str(&text[prev..start]); + } + buf.push_str(&indel.insert); + prev = end; + } + buf.push_str(&text[prev..text.len()]); + assert_eq!(TextSize::of(&buf), total_len); + + // FIXME: figure out a way to mutate the text in-place or reuse the + // memory in some other way + *text = buf + } + + pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> { + // FIXME: can be done without allocating intermediate vector + let mut all = self.iter().chain(other.iter()).collect::>(); + if !check_disjoint(&mut all) { + return Err(other); + } + self.indels.extend(other.indels); + assert!(check_disjoint(&mut self.indels)); + Ok(()) + } + + pub fn apply_to_offset(&self, offset: TextSize) -> Option { + let mut res = offset; + for indel in self.indels.iter() { + if indel.delete.start() >= offset { + break; + } + if offset < indel.delete.end() { + return None; + } + res += TextSize::of(&indel.insert); + res -= indel.delete.len(); + } + Some(res) + } +} + +impl IntoIterator for TextEdit { + type Item = Indel; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.indels.into_iter() + } +} + +impl<'a> IntoIterator for &'a TextEdit { + type Item = &'a Indel; + type IntoIter = std::slice::Iter<'a, Indel>; + + fn into_iter(self) -> Self::IntoIter { + self.indels.iter() + } +} + +impl TextEditBuilder { + pub fn replace(&mut self, range: TextRange, replace_with: String) { + self.indels.push(Indel::replace(range, replace_with)) + } + pub fn delete(&mut self, range: TextRange) { + self.indels.push(Indel::delete(range)) + } + pub fn insert(&mut self, offset: TextSize, text: String) { + self.indels.push(Indel::insert(offset, text)) + } + pub fn finish(self) -> TextEdit { + let mut indels = self.indels; + assert!(check_disjoint(&mut indels)); + TextEdit { indels } + } + pub fn invalidates_offset(&self, offset: TextSize) -> bool { + self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) + } +} + +fn check_disjoint(indels: &mut [impl std::borrow::Borrow]) -> bool { + indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end())); + indels + .iter() + .zip(indels.iter().skip(1)) + .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start()) +} -- cgit v1.2.3