@@ -6,9 +6,12 @@ mod subscriptions;
66pub ( crate ) mod pending_requests;
77
88use std:: {
9+ borrow:: Cow ,
910 env,
1011 error:: Error ,
11- fmt, panic,
12+ fmt,
13+ ops:: Range ,
14+ panic,
1215 path:: PathBuf ,
1316 sync:: Arc ,
1417 time:: { Duration , Instant } ,
@@ -18,11 +21,12 @@ use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
1821use itertools:: Itertools ;
1922use lsp_server:: { Connection , ErrorCode , Message , Notification , Request , RequestId , Response } ;
2023use lsp_types:: {
21- NumberOrString , WorkDoneProgress , WorkDoneProgressBegin , WorkDoneProgressCreateParams ,
22- WorkDoneProgressEnd , WorkDoneProgressReport ,
24+ DidChangeTextDocumentParams , NumberOrString , TextDocumentContentChangeEvent , WorkDoneProgress ,
25+ WorkDoneProgressBegin , WorkDoneProgressCreateParams , WorkDoneProgressEnd ,
26+ WorkDoneProgressReport ,
2327} ;
2428use ra_flycheck:: { url_from_path_with_drive_lowercasing, CheckTask } ;
25- use ra_ide:: { Canceled , FileId , LibraryData , SourceRootId } ;
29+ use ra_ide:: { Canceled , FileId , LibraryData , LineIndex , SourceRootId } ;
2630use ra_prof:: profile;
2731use ra_project_model:: { PackageRoot , ProjectWorkspace } ;
2832use ra_vfs:: { VfsFile , VfsTask , Watch } ;
@@ -33,6 +37,7 @@ use threadpool::ThreadPool;
3337
3438use crate :: {
3539 config:: { Config , FilesWatcher } ,
40+ conv:: { ConvWith , TryConvWith } ,
3641 diagnostics:: DiagnosticTask ,
3742 main_loop:: {
3843 pending_requests:: { PendingRequest , PendingRequests } ,
@@ -579,12 +584,16 @@ fn on_notification(
579584 Err ( not) => not,
580585 } ;
581586 let not = match notification_cast :: < req:: DidChangeTextDocument > ( not) {
582- Ok ( mut params) => {
583- let uri = params. text_document . uri ;
587+ Ok ( params) => {
588+ let DidChangeTextDocumentParams { text_document, content_changes } = params;
589+ let world = state. snapshot ( ) ;
590+ let file_id = text_document. try_conv_with ( & world) ?;
591+ let line_index = world. analysis ( ) . file_line_index ( file_id) ?;
592+ let uri = text_document. uri ;
584593 let path = uri. to_file_path ( ) . map_err ( |( ) | format ! ( "invalid uri: {}" , uri) ) ?;
585- let text =
586- params . content_changes . pop ( ) . ok_or_else ( || "empty changes" . to_string ( ) ) ? . text ;
587- state . vfs . write ( ) . change_file_overlay ( path . as_path ( ) , text ) ;
594+ state . vfs . write ( ) . change_file_overlay ( & path , |old_text| {
595+ apply_document_changes ( old_text , Cow :: Borrowed ( & line_index ) , content_changes ) ;
596+ } ) ;
588597 return Ok ( ( ) ) ;
589598 }
590599 Err ( not) => not,
@@ -653,6 +662,48 @@ fn on_notification(
653662 Ok ( ( ) )
654663}
655664
665+ fn apply_document_changes (
666+ old_text : & mut String ,
667+ mut line_index : Cow < ' _ , LineIndex > ,
668+ content_changes : Vec < TextDocumentContentChangeEvent > ,
669+ ) {
670+ // The changes we got must be applied sequentially, but can cross lines so we
671+ // have to keep our line index updated.
672+ // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
673+ // remember the last valid line in the index and only rebuild it if needed.
674+ enum IndexValid {
675+ All ,
676+ UpToLine ( u64 ) ,
677+ }
678+
679+ impl IndexValid {
680+ fn covers ( & self , line : u64 ) -> bool {
681+ match * self {
682+ IndexValid :: UpToLine ( to) => to >= line,
683+ _ => true ,
684+ }
685+ }
686+ }
687+
688+ let mut index_valid = IndexValid :: All ;
689+ for change in content_changes {
690+ match change. range {
691+ Some ( range) => {
692+ if !index_valid. covers ( range. start . line ) {
693+ line_index = Cow :: Owned ( LineIndex :: new ( & old_text) ) ;
694+ }
695+ index_valid = IndexValid :: UpToLine ( range. start . line ) ;
696+ let range = range. conv_with ( & line_index) ;
697+ old_text. replace_range ( Range :: < usize > :: from ( range) , & change. text ) ;
698+ }
699+ None => {
700+ * old_text = change. text ;
701+ index_valid = IndexValid :: UpToLine ( 0 ) ;
702+ }
703+ }
704+ }
705+ }
706+
656707fn on_check_task (
657708 task : CheckTask ,
658709 world_state : & mut WorldState ,
@@ -958,3 +1009,64 @@ where
9581009{
9591010 Request :: new ( id, R :: METHOD . to_string ( ) , params)
9601011}
1012+
1013+ #[ cfg( test) ]
1014+ mod tests {
1015+ use std:: borrow:: Cow ;
1016+
1017+ use lsp_types:: { Position , Range , TextDocumentContentChangeEvent } ;
1018+ use ra_ide:: LineIndex ;
1019+
1020+ #[ test]
1021+ fn apply_document_changes ( ) {
1022+ fn run ( text : & mut String , changes : Vec < TextDocumentContentChangeEvent > ) {
1023+ let line_index = Cow :: Owned ( LineIndex :: new ( & text) ) ;
1024+ super :: apply_document_changes ( text, line_index, changes) ;
1025+ }
1026+
1027+ macro_rules! c {
1028+ [ $( $sl: expr, $sc: expr; $el: expr, $ec: expr => $text: expr) ,+] => {
1029+ vec![ $( TextDocumentContentChangeEvent {
1030+ range: Some ( Range {
1031+ start: Position { line: $sl, character: $sc } ,
1032+ end: Position { line: $el, character: $ec } ,
1033+ } ) ,
1034+ range_length: None ,
1035+ text: String :: from( $text) ,
1036+ } ) ,+]
1037+ } ;
1038+ }
1039+
1040+ let mut text = String :: new ( ) ;
1041+ run ( & mut text, vec ! [ ] ) ;
1042+ assert_eq ! ( text, "" ) ;
1043+ run (
1044+ & mut text,
1045+ vec ! [ TextDocumentContentChangeEvent {
1046+ range: None ,
1047+ range_length: None ,
1048+ text: String :: from( "the" ) ,
1049+ } ] ,
1050+ ) ;
1051+ assert_eq ! ( text, "the" ) ;
1052+ run ( & mut text, c ! [ 0 , 3 ; 0 , 3 => " quick" ] ) ;
1053+ assert_eq ! ( text, "the quick" ) ;
1054+ run ( & mut text, c ! [ 0 , 0 ; 0 , 4 => "" , 0 , 5 ; 0 , 5 => " foxes" ] ) ;
1055+ assert_eq ! ( text, "quick foxes" ) ;
1056+ run ( & mut text, c ! [ 0 , 11 ; 0 , 11 => "\n dream" ] ) ;
1057+ assert_eq ! ( text, "quick foxes\n dream" ) ;
1058+ run ( & mut text, c ! [ 1 , 0 ; 1 , 0 => "have " ] ) ;
1059+ assert_eq ! ( text, "quick foxes\n have dream" ) ;
1060+ run ( & mut text, c ! [ 0 , 0 ; 0 , 0 => "the " , 1 , 4 ; 1 , 4 => " quiet" , 1 , 16 ; 1 , 16 => "s\n " ] ) ;
1061+ assert_eq ! ( text, "the quick foxes\n have quiet dreams\n " ) ;
1062+ run ( & mut text, c ! [ 0 , 15 ; 0 , 15 => "\n " , 2 , 17 ; 2 , 17 => "\n " ] ) ;
1063+ assert_eq ! ( text, "the quick foxes\n \n have quiet dreams\n \n " ) ;
1064+ run (
1065+ & mut text,
1066+ c ! [ 1 , 0 ; 1 , 0 => "DREAM" , 2 , 0 ; 2 , 0 => "they " , 3 , 0 ; 3 , 0 => "DON'T THEY?" ] ,
1067+ ) ;
1068+ assert_eq ! ( text, "the quick foxes\n DREAM\n they have quiet dreams\n DON'T THEY?\n " ) ;
1069+ run ( & mut text, c ! [ 0 , 10 ; 1 , 5 => "" , 2 , 0 ; 2 , 12 => "" ] ) ;
1070+ assert_eq ! ( text, "the quick \n they have quiet dreams\n " ) ;
1071+ }
1072+ }
0 commit comments