]> git.lizzy.rs Git - rust.git/commitdiff
Rename ra_text_edit -> text_edit
authorAleksey Kladov <aleksey.kladov@gmail.com>
Wed, 12 Aug 2020 15:03:06 +0000 (17:03 +0200)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Wed, 12 Aug 2020 15:03:06 +0000 (17:03 +0200)
31 files changed:
Cargo.lock
crates/ra_assists/Cargo.toml
crates/ra_assists/src/assist_context.rs
crates/ra_assists/src/utils/insert_use.rs
crates/ra_ide/Cargo.toml
crates/ra_ide/src/completion/complete_postfix.rs
crates/ra_ide/src/completion/complete_trait_impl.rs
crates/ra_ide/src/completion/completion_context.rs
crates/ra_ide/src/completion/completion_item.rs
crates/ra_ide/src/diagnostics.rs
crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
crates/ra_ide/src/join_lines.rs
crates/ra_ide/src/lib.rs
crates/ra_ide/src/references/rename.rs
crates/ra_ide/src/typing.rs
crates/ra_ide/src/typing/on_enter.rs
crates/ra_ide_db/Cargo.toml
crates/ra_ide_db/src/source_change.rs
crates/ra_ssr/Cargo.toml
crates/ra_ssr/src/replacing.rs
crates/ra_syntax/Cargo.toml
crates/ra_syntax/fuzz/Cargo.toml
crates/ra_syntax/src/algo.rs
crates/ra_syntax/src/fuzz.rs
crates/ra_syntax/src/lib.rs
crates/ra_syntax/src/parsing/reparsing.rs
crates/ra_text_edit/Cargo.toml [deleted file]
crates/ra_text_edit/src/lib.rs [deleted file]
crates/rust-analyzer/Cargo.toml
crates/text_edit/Cargo.toml [new file with mode: 0644]
crates/text_edit/src/lib.rs [new file with mode: 0644]

index daa872546405ebedc0f93842a3c0e0c5774caf12..4a6a6593415bcb76cbcf83c92ff37f6f8480d9bf 100644 (file)
@@ -927,10 +927,10 @@ dependencies = [
  "ra_hir",
  "ra_ide_db",
  "ra_syntax",
- "ra_text_edit",
  "rustc-hash",
  "stdx",
  "test_utils",
+ "text_edit",
 ]
 
 [[package]]
@@ -1075,10 +1075,10 @@ dependencies = [
  "ra_ide_db",
  "ra_ssr",
  "ra_syntax",
- "ra_text_edit",
  "rustc-hash",
  "stdx",
  "test_utils",
+ "text_edit",
 ]
 
 [[package]]
@@ -1093,11 +1093,11 @@ dependencies = [
  "ra_db",
  "ra_hir",
  "ra_syntax",
- "ra_text_edit",
  "rayon",
  "rustc-hash",
  "stdx",
  "test_utils",
+ "text_edit",
 ]
 
 [[package]]
@@ -1177,9 +1177,9 @@ dependencies = [
  "ra_hir",
  "ra_ide_db",
  "ra_syntax",
- "ra_text_edit",
  "rustc-hash",
  "test_utils",
+ "text_edit",
 ]
 
 [[package]]
@@ -1191,7 +1191,6 @@ dependencies = [
  "itertools",
  "once_cell",
  "ra_parser",
- "ra_text_edit",
  "rayon",
  "rowan",
  "rustc-ap-rustc_lexer",
@@ -1200,16 +1199,10 @@ dependencies = [
  "smol_str",
  "stdx",
  "test_utils",
+ "text_edit",
  "walkdir",
 ]
 
-[[package]]
-name = "ra_text_edit"
-version = "0.0.0"
-dependencies = [
- "text-size",
-]
-
 [[package]]
 name = "rayon"
 version = "1.3.1"
@@ -1312,13 +1305,13 @@ dependencies = [
  "ra_project_model",
  "ra_ssr",
  "ra_syntax",
- "ra_text_edit",
  "rayon",
  "rustc-hash",
  "serde",
  "serde_json",
  "stdx",
  "test_utils",
+ "text_edit",
  "threadpool",
  "toolchain",
  "tt",
@@ -1565,6 +1558,13 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a"
 
+[[package]]
+name = "text_edit"
+version = "0.0.0"
+dependencies = [
+ "text-size",
+]
+
 [[package]]
 name = "thin-dst"
 version = "1.1.0"
index 6f5ace941fbe967a08b79cb211ee94238937c28b..e4a5ee6c1c80623a332534591a5e049f6610f14d 100644 (file)
@@ -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" }
index afba860d1dcf7c8c326cfd5e3f85965d549d8202..fcaa1aedcf3699ae0fe1e8efb203f788ffdb3f83 100644 (file)
@@ -15,7 +15,7 @@
     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},
index 32780fceb59b997c12c0e237eb6f43ec42cca320..13dbe1919c19382c0fa95e6a90fa4f945d1b09b5 100644 (file)
@@ -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 @@
     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(
index bbc9ba4e77d939bb8d058698ab7043068840f5d9..84c25f0b8a2efd5a6ad34ccf365cbf9df2bcb7ee 100644 (file)
@@ -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" }
index 8735b9010370b30a1fa35625d320b081c42f482e..42087da8dc5273d0e7c6e83bcde13c3a0ad1dac8 100644 (file)
@@ -4,7 +4,7 @@
     ast::{self, AstNode},
     TextRange, TextSize,
 };
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 use crate::{
     completion::{
index d9a0ef167dbdc7cb2cc3e9ba614e6afa896fd08e..b397baf10787f677b951404732ed8d39d64df84b 100644 (file)
@@ -37,7 +37,7 @@
     ast::{self, edit, Impl},
     AstNode, SyntaxKind, SyntaxNode, TextRange, T,
 };
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 use crate::{
     completion::{
index 4aa761148d80aa868cd66a1a88635819c3f9cce4..0cb57fb1b42c3ad60996da62986d068d08e70892 100644 (file)
@@ -9,7 +9,7 @@
     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,
index 7bdda316c458dbc76091fbeb144bc12d6a39709e..1c0684f4ed58e798fab6705ce6d64043a3a85ceb 100644 (file)
@@ -4,7 +4,7 @@
 
 use hir::Documentation;
 use ra_syntax::TextRange;
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 use crate::completion::completion_config::SnippetCap;
 
index e006c777548583699ac11534f19b9972ff9ec1d1..54810d5bbf021aa3b97a5a2ad68a5211b9f332cb 100644 (file)
@@ -14,7 +14,7 @@
     ast::{self, AstNode},
     SyntaxNode, TextRange, T,
 };
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 use crate::{Diagnostic, FileId, Fix, SourceFileEdit};
 
index 88e593e0039daf89b2306fe91d3a73f31ffb5d44..8fb25de6c16c43236d82b27820d951b09eca0322 100644 (file)
@@ -13,7 +13,7 @@
     RootDatabase,
 };
 use ra_syntax::{algo, ast, AstNode};
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 /// A [Diagnostic] that potentially has a fix available.
 ///
index 1c881386f7dd90bb6677d9f1ad1f4be5e1b00489..caf63933a9be946cab315f133eb5aed9b7c3749b 100644 (file)
@@ -7,7 +7,7 @@
     SyntaxKind::{self, WHITESPACE},
     SyntaxNode, SyntaxToken, TextRange, TextSize, T,
 };
-use ra_text_edit::{TextEdit, TextEditBuilder};
+use text_edit::{TextEdit, TextEditBuilder};
 
 // Feature: Join Lines
 //
index bfcf5d750a4aa00736f270cbf5345b38d6497d10..09cb5faf68fba42c45cc1505af7942c604c513f7 100644 (file)
@@ -96,7 +96,7 @@ macro_rules! eprintln {
     RootDatabase,
 };
 pub use ra_ssr::SsrError;
-pub use ra_text_edit::{Indel, TextEdit};
+pub use text_edit::{Indel, TextEdit};
 
 pub type Cancelable<T> = Result<T, Canceled>;
 
index 8c1ac3c5601a18965f718d75d41a090ff9e3fc2d..9c688fb063171c69ccb47aeb786adb87380fcda2 100644 (file)
@@ -11,9 +11,9 @@
     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};
 
index d3ce744b44cb731d1732439bb0cf490032b4cfc7..952429cde6517418e250ec4aa953a372e9fa576d 100644 (file)
@@ -26,7 +26,7 @@
     TextRange, TextSize,
 };
 
-use ra_text_edit::TextEdit;
+use text_edit::TextEdit;
 
 use crate::SourceChange;
 
index 143b1ae413ed1ce380fd5fc0d968e1dfd01018d2..c0c5ce3bcd29c168df26b927a147fc3782924c83 100644 (file)
@@ -9,8 +9,8 @@
     SyntaxKind::*,
     SyntaxToken, TextRange, TextSize, TokenAtOffset,
 };
-use ra_text_edit::TextEdit;
 use test_utils::mark;
+use text_edit::TextEdit;
 
 // Feature: On Enter
 //
index 92b8ef82a7a9ee1c3b5987d74b89dca9dcfbdfe1..5446a596141dd4b5cb1480f6a7be8c4ce98bf751 100644 (file)
@@ -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" }
index abb83f421343d0fc6c71ff579da0e0d374bae260..ae21132dd71e980db4710c014e9bc0ba526b363e 100644 (file)
@@ -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 {
index 84e4b171e1beb4bc4b729334c10695719ba1cf47..d0f2ae73393f79e5f8e67202a01d66fb959cc24a 100644 (file)
@@ -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" }
index 36ced3842da136c53b7d843dbc126a3a3320c391..74f9e7db6167aa8e9781eb581e15073145436229 100644 (file)
@@ -4,8 +4,8 @@
 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
index fc4d7aa048b80551460d2c2df9639cf4d3e7b58d..f2789e6a351a46c72dc5f59ce336e007ef5fd7bb 100644 (file)
@@ -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`.
index 613ad2857d00cea160247df48f8c9bdeebac2ea5..4cec3c4cd8bf744c2379bd80d391825ff849916c 100644 (file)
@@ -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
index 26b3c813a1d6060cd60f161d56b080b4f9bcc793..6254b38ba1c6eb243ad6e2a7f4adf03007437132 100644 (file)
@@ -6,8 +6,8 @@
 };
 
 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,
index 39f9b12ab20b916e1760129ce5940f5e6665537b..fbb97aa27369e840b0d4fa2ce719e24bc0a47017 100644 (file)
@@ -5,7 +5,7 @@
     str::{self, FromStr},
 };
 
-use ra_text_edit::Indel;
+use text_edit::Indel;
 
 use crate::{validation, AstNode, SourceFile, TextRange};
 
index 8a4d4538622bf2db4664c4140cc5bd4eadeb0867..465607f550f73efb50170868e693e1cea5c781b0 100644 (file)
@@ -39,8 +39,8 @@ macro_rules! eprintln {
 
 use std::{marker::PhantomData, sync::Arc};
 
-use ra_text_edit::Indel;
 use stdx::format_to;
+use text_edit::Indel;
 
 pub use crate::{
     algo::InsertPosition,
index ed5a42ea388dae2b53216b136a9853b16a392d5b..6644ffca4fa7575e4eda7a81d1f6d9295a509332 100644 (file)
@@ -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 (file)
index 427862a..0000000
+++ /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 (file)
index ab8cd7f..0000000
+++ /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<Indel>,
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct TextEditBuilder {
-    indels: Vec<Indel>,
-}
-
-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::<Vec<_>>();
-        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<TextSize> {
-        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<Indel>;
-
-    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<Indel>]) -> 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())
-}
index 3f70510fd76e3cc34ccd266f9aa3efabb4268a5f..ef244da597051f5b3ee79c70143ae81150b4b60d 100644 (file)
@@ -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 (file)
index 0000000..a69b1ef
--- /dev/null
@@ -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 (file)
index 0000000..ab8cd7f
--- /dev/null
@@ -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<Indel>,
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct TextEditBuilder {
+    indels: Vec<Indel>,
+}
+
+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::<Vec<_>>();
+        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<TextSize> {
+        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<Indel>;
+
+    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<Indel>]) -> 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())
+}