aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs117
-rw-r--r--crates/rust-analyzer/src/main_loop.rs126
2 files changed, 123 insertions, 120 deletions
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 078f8778e..14adb8ae7 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,9 +1,11 @@
1//! Utilities for LSP-related boilerplate code. 1//! Utilities for LSP-related boilerplate code.
2use std::error::Error; 2use std::{error::Error, ops::Range};
3 3
4use crate::from_proto;
4use crossbeam_channel::Sender; 5use crossbeam_channel::Sender;
5use lsp_server::{Message, Notification}; 6use lsp_server::{Message, Notification};
6use ra_db::Canceled; 7use ra_db::Canceled;
8use ra_ide::LineIndex;
7use serde::{de::DeserializeOwned, Serialize}; 9use serde::{de::DeserializeOwned, Serialize};
8 10
9pub fn show_message( 11pub fn show_message(
@@ -42,3 +44,116 @@ where
42{ 44{
43 Notification::new(N::METHOD.to_string(), params) 45 Notification::new(N::METHOD.to_string(), params)
44} 46}
47
48pub(crate) fn apply_document_changes(
49 old_text: &mut String,
50 content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
51) {
52 let mut line_index = LineIndex::new(old_text);
53 // The changes we got must be applied sequentially, but can cross lines so we
54 // have to keep our line index updated.
55 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
56 // remember the last valid line in the index and only rebuild it if needed.
57 // The VFS will normalize the end of lines to `\n`.
58 enum IndexValid {
59 All,
60 UpToLineExclusive(u64),
61 }
62
63 impl IndexValid {
64 fn covers(&self, line: u64) -> bool {
65 match *self {
66 IndexValid::UpToLineExclusive(to) => to > line,
67 _ => true,
68 }
69 }
70 }
71
72 let mut index_valid = IndexValid::All;
73 for change in content_changes {
74 match change.range {
75 Some(range) => {
76 if !index_valid.covers(range.end.line) {
77 line_index = LineIndex::new(&old_text);
78 }
79 index_valid = IndexValid::UpToLineExclusive(range.start.line);
80 let range = from_proto::text_range(&line_index, range);
81 old_text.replace_range(Range::<usize>::from(range), &change.text);
82 }
83 None => {
84 *old_text = change.text;
85 index_valid = IndexValid::UpToLineExclusive(0);
86 }
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
94
95 use super::*;
96
97 #[test]
98 fn test_apply_document_changes() {
99 macro_rules! c {
100 [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
101 vec![$(TextDocumentContentChangeEvent {
102 range: Some(Range {
103 start: Position { line: $sl, character: $sc },
104 end: Position { line: $el, character: $ec },
105 }),
106 range_length: None,
107 text: String::from($text),
108 }),+]
109 };
110 }
111
112 let mut text = String::new();
113 apply_document_changes(&mut text, vec![]);
114 assert_eq!(text, "");
115 apply_document_changes(
116 &mut text,
117 vec![TextDocumentContentChangeEvent {
118 range: None,
119 range_length: None,
120 text: String::from("the"),
121 }],
122 );
123 assert_eq!(text, "the");
124 apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
125 assert_eq!(text, "the quick");
126 apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
127 assert_eq!(text, "quick foxes");
128 apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
129 assert_eq!(text, "quick foxes\ndream");
130 apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
131 assert_eq!(text, "quick foxes\nhave dream");
132 apply_document_changes(
133 &mut text,
134 c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
135 );
136 assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
137 apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
138 assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
139 apply_document_changes(
140 &mut text,
141 c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
142 );
143 assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
144 apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
145 assert_eq!(text, "the quick \nthey have quiet dreams\n");
146
147 text = String::from("❤️");
148 apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
149 assert_eq!(text, "a❤️");
150
151 text = String::from("a\nb");
152 apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
153 assert_eq!(text, "adcb");
154
155 text = String::from("a\nb");
156 apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
157 assert_eq!(text, "ațc\ncb");
158 }
159}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 200641cd5..3b3b83209 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -1,18 +1,16 @@
1//! The main loop of `rust-analyzer` responsible for dispatching LSP 1//! The main loop of `rust-analyzer` responsible for dispatching LSP
2//! requests/replies and notifications back to the client. 2//! requests/replies and notifications back to the client.
3use std::{ 3use std::{
4 env, fmt, 4 env, fmt, panic,
5 ops::Range,
6 panic,
7 sync::Arc, 5 sync::Arc,
8 time::{Duration, Instant}, 6 time::{Duration, Instant},
9}; 7};
10 8
11use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 9use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
12use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 10use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
13use lsp_types::{request::Request as _, NumberOrString, TextDocumentContentChangeEvent}; 11use lsp_types::{request::Request as _, NumberOrString};
14use ra_db::VfsPath; 12use ra_db::VfsPath;
15use ra_ide::{Canceled, FileId, LineIndex}; 13use ra_ide::{Canceled, FileId};
16use ra_prof::profile; 14use ra_prof::profile;
17use ra_project_model::{PackageRoot, ProjectWorkspace}; 15use ra_project_model::{PackageRoot, ProjectWorkspace};
18use serde::{de::DeserializeOwned, Serialize}; 16use serde::{de::DeserializeOwned, Serialize};
@@ -24,7 +22,10 @@ use crate::{
24 from_proto, 22 from_proto,
25 global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status}, 23 global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status},
26 handlers, lsp_ext, 24 handlers, lsp_ext,
27 lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, show_message}, 25 lsp_utils::{
26 apply_document_changes, is_canceled, notification_cast, notification_is, notification_new,
27 show_message,
28 },
28 request_metrics::RequestMetrics, 29 request_metrics::RequestMetrics,
29 LspError, Result, 30 LspError, Result,
30}; 31};
@@ -548,49 +549,6 @@ fn on_notification(
548 Ok(()) 549 Ok(())
549} 550}
550 551
551fn apply_document_changes(
552 old_text: &mut String,
553 content_changes: Vec<TextDocumentContentChangeEvent>,
554) {
555 let mut line_index = LineIndex::new(old_text);
556 // The changes we got must be applied sequentially, but can cross lines so we
557 // have to keep our line index updated.
558 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
559 // remember the last valid line in the index and only rebuild it if needed.
560 // The VFS will normalize the end of lines to `\n`.
561 enum IndexValid {
562 All,
563 UpToLineExclusive(u64),
564 }
565
566 impl IndexValid {
567 fn covers(&self, line: u64) -> bool {
568 match *self {
569 IndexValid::UpToLineExclusive(to) => to > line,
570 _ => true,
571 }
572 }
573 }
574
575 let mut index_valid = IndexValid::All;
576 for change in content_changes {
577 match change.range {
578 Some(range) => {
579 if !index_valid.covers(range.end.line) {
580 line_index = LineIndex::new(&old_text);
581 }
582 index_valid = IndexValid::UpToLineExclusive(range.start.line);
583 let range = from_proto::text_range(&line_index, range);
584 old_text.replace_range(Range::<usize>::from(range), &change.text);
585 }
586 None => {
587 *old_text = change.text;
588 index_valid = IndexValid::UpToLineExclusive(0);
589 }
590 }
591 }
592}
593
594fn on_check_task( 552fn on_check_task(
595 task: flycheck::Message, 553 task: flycheck::Message,
596 global_state: &mut GlobalState, 554 global_state: &mut GlobalState,
@@ -862,73 +820,3 @@ fn update_file_notifications_on_threadpool(
862 }) 820 })
863 } 821 }
864} 822}
865
866#[cfg(test)]
867mod tests {
868 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
869
870 use super::*;
871
872 #[test]
873 fn test_apply_document_changes() {
874 macro_rules! c {
875 [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
876 vec![$(TextDocumentContentChangeEvent {
877 range: Some(Range {
878 start: Position { line: $sl, character: $sc },
879 end: Position { line: $el, character: $ec },
880 }),
881 range_length: None,
882 text: String::from($text),
883 }),+]
884 };
885 }
886
887 let mut text = String::new();
888 apply_document_changes(&mut text, vec![]);
889 assert_eq!(text, "");
890 apply_document_changes(
891 &mut text,
892 vec![TextDocumentContentChangeEvent {
893 range: None,
894 range_length: None,
895 text: String::from("the"),
896 }],
897 );
898 assert_eq!(text, "the");
899 apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
900 assert_eq!(text, "the quick");
901 apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
902 assert_eq!(text, "quick foxes");
903 apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
904 assert_eq!(text, "quick foxes\ndream");
905 apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
906 assert_eq!(text, "quick foxes\nhave dream");
907 apply_document_changes(
908 &mut text,
909 c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
910 );
911 assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
912 apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
913 assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
914 apply_document_changes(
915 &mut text,
916 c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
917 );
918 assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
919 apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
920 assert_eq!(text, "the quick \nthey have quiet dreams\n");
921
922 text = String::from("❤️");
923 apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
924 assert_eq!(text, "a❤️");
925
926 text = String::from("a\nb");
927 apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
928 assert_eq!(text, "adcb");
929
930 text = String::from("a\nb");
931 apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
932 assert_eq!(text, "ațc\ncb");
933 }
934}