1 //! Utilities for LSP-related boilerplate code.
2 use std::{error::Error, ops::Range};
5 use ide_db::base_db::Canceled;
6 use lsp_server::Notification;
8 use crate::{from_proto, global_state::GlobalState};
10 pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
11 e.downcast_ref::<Canceled>().is_some()
14 pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
15 notification: &Notification,
17 notification.method == N::METHOD
20 #[derive(Debug, Eq, PartialEq)]
21 pub(crate) enum Progress {
28 pub(crate) fn fraction(done: usize, total: usize) -> f64 {
29 assert!(done <= total);
30 done as f64 / total.max(1) as f64
35 pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
36 let message = message.into();
37 self.send_notification::<lsp_types::notification::ShowMessage>(
38 lsp_types::ShowMessageParams { typ, message },
42 pub(crate) fn report_progress(
46 message: Option<String>,
47 fraction: Option<f64>,
49 if !self.config.client_caps.work_done_progress {
52 let percentage = fraction.map(|f| {
53 assert!(0.0 <= f && f <= 1.0);
56 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
57 let work_done_progress = match state {
59 self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
60 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
64 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
72 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
79 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
82 self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
84 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
89 pub(crate) fn apply_document_changes(
90 old_text: &mut String,
91 content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
93 let mut line_index = LineIndex::new(old_text);
94 // The changes we got must be applied sequentially, but can cross lines so we
95 // have to keep our line index updated.
96 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
97 // remember the last valid line in the index and only rebuild it if needed.
98 // The VFS will normalize the end of lines to `\n`.
101 UpToLineExclusive(u64),
105 fn covers(&self, line: u64) -> bool {
107 IndexValid::UpToLineExclusive(to) => to > line,
113 let mut index_valid = IndexValid::All;
114 for change in content_changes {
117 if !index_valid.covers(range.end.line) {
118 line_index = LineIndex::new(&old_text);
120 index_valid = IndexValid::UpToLineExclusive(range.start.line);
121 let range = from_proto::text_range(&line_index, range);
122 old_text.replace_range(Range::<usize>::from(range), &change.text);
125 *old_text = change.text;
126 index_valid = IndexValid::UpToLineExclusive(0);
134 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
139 fn test_apply_document_changes() {
141 [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
142 vec![$(TextDocumentContentChangeEvent {
144 start: Position { line: $sl, character: $sc },
145 end: Position { line: $el, character: $ec },
148 text: String::from($text),
153 let mut text = String::new();
154 apply_document_changes(&mut text, vec![]);
155 assert_eq!(text, "");
156 apply_document_changes(
158 vec![TextDocumentContentChangeEvent {
161 text: String::from("the"),
164 assert_eq!(text, "the");
165 apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
166 assert_eq!(text, "the quick");
167 apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
168 assert_eq!(text, "quick foxes");
169 apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
170 assert_eq!(text, "quick foxes\ndream");
171 apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
172 assert_eq!(text, "quick foxes\nhave dream");
173 apply_document_changes(
175 c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
177 assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
178 apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
179 assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
180 apply_document_changes(
182 c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
184 assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
185 apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
186 assert_eq!(text, "the quick \nthey have quiet dreams\n");
188 text = String::from("❤️");
189 apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
190 assert_eq!(text, "a❤️");
192 text = String::from("a\nb");
193 apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
194 assert_eq!(text, "adcb");
196 text = String::from("a\nb");
197 apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
198 assert_eq!(text, "ațc\ncb");