2020-05-10 10:35:33 -05:00
|
|
|
|
//! Utilities for LSP-related boilerplate code.
|
2020-08-03 15:35:32 -05:00
|
|
|
|
use std::{error::Error, ops::Range};
|
2020-05-10 10:35:33 -05:00
|
|
|
|
|
2020-08-13 10:42:52 -05:00
|
|
|
|
use ide::LineIndex;
|
2020-10-24 03:39:57 -05:00
|
|
|
|
use ide_db::base_db::Canceled;
|
2020-06-26 09:33:57 -05:00
|
|
|
|
use lsp_server::Notification;
|
2020-06-25 10:50:47 -05:00
|
|
|
|
|
2020-06-26 09:33:57 -05:00
|
|
|
|
use crate::{from_proto, global_state::GlobalState};
|
2020-05-09 22:44:02 -05:00
|
|
|
|
|
|
|
|
|
pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
|
|
|
|
|
e.downcast_ref::<Canceled>().is_some()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
|
|
|
|
|
notification: &Notification,
|
|
|
|
|
) -> bool {
|
|
|
|
|
notification.method == N::METHOD
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 09:33:57 -05:00
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
|
pub(crate) enum Progress {
|
|
|
|
|
Begin,
|
|
|
|
|
Report,
|
|
|
|
|
End,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Progress {
|
2020-10-07 05:15:37 -05:00
|
|
|
|
pub(crate) fn fraction(done: usize, total: usize) -> f64 {
|
|
|
|
|
assert!(done <= total);
|
|
|
|
|
done as f64 / total.max(1) as f64
|
2020-06-26 09:33:57 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GlobalState {
|
|
|
|
|
pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
|
|
|
|
|
let message = message.into();
|
2020-06-26 10:07:14 -05:00
|
|
|
|
self.send_notification::<lsp_types::notification::ShowMessage>(
|
|
|
|
|
lsp_types::ShowMessageParams { typ, message },
|
|
|
|
|
)
|
2020-06-26 09:33:57 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn report_progress(
|
|
|
|
|
&mut self,
|
|
|
|
|
title: &str,
|
|
|
|
|
state: Progress,
|
|
|
|
|
message: Option<String>,
|
2020-10-07 05:15:37 -05:00
|
|
|
|
fraction: Option<f64>,
|
2020-06-26 09:33:57 -05:00
|
|
|
|
) {
|
|
|
|
|
if !self.config.client_caps.work_done_progress {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-07 05:15:37 -05:00
|
|
|
|
let percentage = fraction.map(|f| {
|
|
|
|
|
assert!(0.0 <= f && f <= 1.0);
|
2020-11-16 14:10:13 -06:00
|
|
|
|
(f * 100.0) as u32
|
2020-10-07 05:15:37 -05:00
|
|
|
|
});
|
2020-06-26 09:33:57 -05:00
|
|
|
|
let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
|
|
|
|
|
let work_done_progress = match state {
|
|
|
|
|
Progress::Begin => {
|
2020-06-26 10:07:14 -05:00
|
|
|
|
self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
|
2020-06-26 09:33:57 -05:00
|
|
|
|
lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
|
|
|
|
|
|_, _| (),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
|
|
|
|
|
title: title.into(),
|
|
|
|
|
cancellable: None,
|
|
|
|
|
message,
|
|
|
|
|
percentage,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
Progress::Report => {
|
|
|
|
|
lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
|
|
|
|
|
cancellable: None,
|
|
|
|
|
message,
|
|
|
|
|
percentage,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
Progress::End => {
|
|
|
|
|
lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
|
|
|
|
|
}
|
|
|
|
|
};
|
2020-06-26 10:07:14 -05:00
|
|
|
|
self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
|
|
|
|
|
token,
|
|
|
|
|
value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
|
|
|
|
|
});
|
2020-06-26 09:33:57 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-25 02:51:30 -05:00
|
|
|
|
pub(crate) fn apply_document_changes(
|
|
|
|
|
old_text: &mut String,
|
|
|
|
|
content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
|
|
|
|
|
) {
|
2020-08-03 15:35:32 -05:00
|
|
|
|
let mut line_index = LineIndex::new(old_text);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
// The changes we got must be applied sequentially, but can cross lines so we
|
|
|
|
|
// have to keep our line index updated.
|
|
|
|
|
// Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
|
|
|
|
|
// remember the last valid line in the index and only rebuild it if needed.
|
|
|
|
|
// The VFS will normalize the end of lines to `\n`.
|
|
|
|
|
enum IndexValid {
|
|
|
|
|
All,
|
2020-11-16 14:10:13 -06:00
|
|
|
|
UpToLineExclusive(u32),
|
2020-06-25 02:51:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IndexValid {
|
2020-11-16 14:10:13 -06:00
|
|
|
|
fn covers(&self, line: u32) -> bool {
|
2020-06-25 02:51:30 -05:00
|
|
|
|
match *self {
|
|
|
|
|
IndexValid::UpToLineExclusive(to) => to > line,
|
|
|
|
|
_ => true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut index_valid = IndexValid::All;
|
|
|
|
|
for change in content_changes {
|
|
|
|
|
match change.range {
|
|
|
|
|
Some(range) => {
|
|
|
|
|
if !index_valid.covers(range.end.line) {
|
2020-08-03 15:35:32 -05:00
|
|
|
|
line_index = LineIndex::new(&old_text);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
}
|
|
|
|
|
index_valid = IndexValid::UpToLineExclusive(range.start.line);
|
|
|
|
|
let range = from_proto::text_range(&line_index, range);
|
|
|
|
|
old_text.replace_range(Range::<usize>::from(range), &change.text);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
*old_text = change.text;
|
|
|
|
|
index_valid = IndexValid::UpToLineExclusive(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_apply_document_changes() {
|
|
|
|
|
macro_rules! c {
|
|
|
|
|
[$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
|
|
|
|
|
vec![$(TextDocumentContentChangeEvent {
|
|
|
|
|
range: Some(Range {
|
|
|
|
|
start: Position { line: $sl, character: $sc },
|
|
|
|
|
end: Position { line: $el, character: $ec },
|
|
|
|
|
}),
|
|
|
|
|
range_length: None,
|
|
|
|
|
text: String::from($text),
|
|
|
|
|
}),+]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut text = String::new();
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, vec![]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(
|
2020-06-25 02:51:30 -05:00
|
|
|
|
&mut text,
|
|
|
|
|
vec![TextDocumentContentChangeEvent {
|
|
|
|
|
range: None,
|
|
|
|
|
range_length: None,
|
|
|
|
|
text: String::from("the"),
|
|
|
|
|
}],
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(text, "the");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "the quick");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "quick foxes");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "quick foxes\ndream");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "quick foxes\nhave dream");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(
|
|
|
|
|
&mut text,
|
|
|
|
|
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
|
|
|
|
|
);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(
|
2020-06-25 02:51:30 -05:00
|
|
|
|
&mut text,
|
|
|
|
|
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "the quick \nthey have quiet dreams\n");
|
|
|
|
|
|
|
|
|
|
text = String::from("❤️");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "a❤️");
|
|
|
|
|
|
|
|
|
|
text = String::from("a\nb");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "adcb");
|
|
|
|
|
|
|
|
|
|
text = String::from("a\nb");
|
2020-08-03 15:35:32 -05:00
|
|
|
|
apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
|
2020-06-25 02:51:30 -05:00
|
|
|
|
assert_eq!(text, "ațc\ncb");
|
|
|
|
|
}
|
|
|
|
|
}
|