1- //! FIXME: write short doc here
2-
3- mod text_edit;
1+ //! Representation of a `TextEdit`.
2+ //!
3+ //! `rust-analyzer` never mutates text itself and only sends diffs to clients,
4+ //! so `TextEdit` is the ultimate representation of the work done by
5+ //! rust-analyzer.
46
57use text_size:: { TextRange , TextSize } ;
68
7- pub use crate :: text_edit :: { TextEdit , TextEditBuilder } ;
8-
9- /// Must not overlap with other `AtomTextEdit `s
9+ /// `InsertDelete` -- a single "atomic" change to text
10+ ///
11+ /// Must not overlap with other `InDel `s
1012#[ derive( Debug , Clone ) ]
11- pub struct AtomTextEdit {
13+ pub struct Indel {
14+ pub insert : String ,
1215 /// Refers to offsets in the original text
1316 pub delete : TextRange ,
14- pub insert : String ,
1517}
1618
17- impl AtomTextEdit {
18- pub fn replace ( range : TextRange , replace_with : String ) -> AtomTextEdit {
19- AtomTextEdit { delete : range , insert : replace_with }
20- }
19+ # [ derive ( Debug , Clone ) ]
20+ pub struct TextEdit {
21+ indels : Vec < Indel > ,
22+ }
2123
22- pub fn delete ( range : TextRange ) -> AtomTextEdit {
23- AtomTextEdit :: replace ( range, String :: new ( ) )
24- }
24+ #[ derive( Debug , Default ) ]
25+ pub struct TextEditBuilder {
26+ indels : Vec < Indel > ,
27+ }
2528
26- pub fn insert ( offset : TextSize , text : String ) -> AtomTextEdit {
27- AtomTextEdit :: replace ( TextRange :: empty ( offset) , text)
29+ impl Indel {
30+ pub fn insert ( offset : TextSize , text : String ) -> Indel {
31+ Indel :: replace ( TextRange :: empty ( offset) , text)
32+ }
33+ pub fn delete ( range : TextRange ) -> Indel {
34+ Indel :: replace ( range, String :: new ( ) )
35+ }
36+ pub fn replace ( range : TextRange , replace_with : String ) -> Indel {
37+ Indel { delete : range, insert : replace_with }
2838 }
2939
3040 pub fn apply ( & self , mut text : String ) -> String {
@@ -34,3 +44,90 @@ impl AtomTextEdit {
3444 text
3545 }
3646}
47+
48+ impl TextEdit {
49+ pub fn insert ( offset : TextSize , text : String ) -> TextEdit {
50+ let mut builder = TextEditBuilder :: default ( ) ;
51+ builder. insert ( offset, text) ;
52+ builder. finish ( )
53+ }
54+
55+ pub fn delete ( range : TextRange ) -> TextEdit {
56+ let mut builder = TextEditBuilder :: default ( ) ;
57+ builder. delete ( range) ;
58+ builder. finish ( )
59+ }
60+
61+ pub fn replace ( range : TextRange , replace_with : String ) -> TextEdit {
62+ let mut builder = TextEditBuilder :: default ( ) ;
63+ builder. replace ( range, replace_with) ;
64+ builder. finish ( )
65+ }
66+
67+ pub ( crate ) fn from_indels ( mut indels : Vec < Indel > ) -> TextEdit {
68+ indels. sort_by_key ( |a| ( a. delete . start ( ) , a. delete . end ( ) ) ) ;
69+ for ( a1, a2) in indels. iter ( ) . zip ( indels. iter ( ) . skip ( 1 ) ) {
70+ assert ! ( a1. delete. end( ) <= a2. delete. start( ) )
71+ }
72+ TextEdit { indels }
73+ }
74+
75+ pub fn as_indels ( & self ) -> & [ Indel ] {
76+ & self . indels
77+ }
78+
79+ pub fn apply ( & self , text : & str ) -> String {
80+ let mut total_len = TextSize :: of ( text) ;
81+ for indel in self . indels . iter ( ) {
82+ total_len += TextSize :: of ( & indel. insert ) ;
83+ total_len -= indel. delete . end ( ) - indel. delete . start ( ) ;
84+ }
85+ let mut buf = String :: with_capacity ( total_len. into ( ) ) ;
86+ let mut prev = 0 ;
87+ for indel in self . indels . iter ( ) {
88+ let start: usize = indel. delete . start ( ) . into ( ) ;
89+ let end: usize = indel. delete . end ( ) . into ( ) ;
90+ if start > prev {
91+ buf. push_str ( & text[ prev..start] ) ;
92+ }
93+ buf. push_str ( & indel. insert ) ;
94+ prev = end;
95+ }
96+ buf. push_str ( & text[ prev..text. len ( ) ] ) ;
97+ assert_eq ! ( TextSize :: of( & buf) , total_len) ;
98+ buf
99+ }
100+
101+ pub fn apply_to_offset ( & self , offset : TextSize ) -> Option < TextSize > {
102+ let mut res = offset;
103+ for indel in self . indels . iter ( ) {
104+ if indel. delete . start ( ) >= offset {
105+ break ;
106+ }
107+ if offset < indel. delete . end ( ) {
108+ return None ;
109+ }
110+ res += TextSize :: of ( & indel. insert ) ;
111+ res -= indel. delete . len ( ) ;
112+ }
113+ Some ( res)
114+ }
115+ }
116+
117+ impl TextEditBuilder {
118+ pub fn replace ( & mut self , range : TextRange , replace_with : String ) {
119+ self . indels . push ( Indel :: replace ( range, replace_with) )
120+ }
121+ pub fn delete ( & mut self , range : TextRange ) {
122+ self . indels . push ( Indel :: delete ( range) )
123+ }
124+ pub fn insert ( & mut self , offset : TextSize , text : String ) {
125+ self . indels . push ( Indel :: insert ( offset, text) )
126+ }
127+ pub fn finish ( self ) -> TextEdit {
128+ TextEdit :: from_indels ( self . indels )
129+ }
130+ pub fn invalidates_offset ( & self , offset : TextSize ) -> bool {
131+ self . indels . iter ( ) . any ( |indel| indel. delete . contains_inclusive ( offset) )
132+ }
133+ }
0 commit comments