1use debug_ignore::DebugIgnore;
2use lsp_types::{
3 InitializeParams, NumberOrString, ProgressParams, ProgressParamsValue, WorkDoneProgress,
4 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
5};
6
7const DOWNLOADING_TOKEN: &str = "downloading-dependencies";
8
9pub trait ProgressReporter {
10 fn compilation_started(&self);
11 fn compilation_finished(&self);
12 fn dependency_downloading_started(&self);
13 fn dependency_downloading_finished(&self);
14}
15
16// Used to publish progress notifications to the client without waiting for
17// the usual request-response loop of the language server.
18#[derive(Debug, Clone)]
19pub struct ConnectionProgressReporter<'a> {
20 connection: DebugIgnore<&'a lsp_server::Connection>,
21}
22
23impl<'a> ConnectionProgressReporter<'a> {
24 pub fn new(
25 connection: &'a lsp_server::Connection,
26 // We don't actually need these but we take them anyway to ensure that
27 // this object is only created after the server has been initialised.
28 // If it was created before then the creation of the progress token
29 // would fail.
30 _initialise_params: &InitializeParams,
31 ) -> Self {
32 create_token(DOWNLOADING_TOKEN, connection);
33 Self {
34 connection: connection.into(),
35 }
36 }
37
38 fn send_notification(&self, token: &str, work_done: WorkDoneProgress) {
39 let params = ProgressParams {
40 token: NumberOrString::String(token.to_string()),
41 value: ProgressParamsValue::WorkDone(work_done),
42 };
43 let notification = lsp_server::Notification {
44 method: "$/progress".into(),
45 params: serde_json::to_value(params).expect("ProgressParams json"),
46 };
47 self.connection
48 .sender
49 .send(lsp_server::Message::Notification(notification))
50 .expect("send_work_done_notification send")
51 }
52}
53
54impl ProgressReporter for ConnectionProgressReporter<'_> {
55 fn compilation_started(&self) {
56 // Do nothing. This is only used for tests currently.
57 // In future we could make this emit a message to the client if compilation is taking a
58 // long time.
59 }
60
61 fn compilation_finished(&self) {
62 // Do nothing. This is only used for tests currently.
63 }
64
65 fn dependency_downloading_started(&self) {
66 let title = "Downloading Gleam dependencies";
67 self.send_notification(DOWNLOADING_TOKEN, begin_message(title));
68 }
69
70 fn dependency_downloading_finished(&self) {
71 self.send_notification(DOWNLOADING_TOKEN, end_message());
72 }
73}
74
75fn end_message() -> WorkDoneProgress {
76 WorkDoneProgress::End(WorkDoneProgressEnd { message: None })
77}
78
79fn begin_message(title: &str) -> WorkDoneProgress {
80 WorkDoneProgress::Begin(WorkDoneProgressBegin {
81 title: title.into(),
82 cancellable: Some(false),
83 message: None,
84 percentage: None,
85 })
86}
87
88fn create_token(token: &str, connection: &lsp_server::Connection) {
89 let params = WorkDoneProgressCreateParams {
90 token: NumberOrString::String(token.into()),
91 };
92 let request = lsp_server::Request {
93 id: format!("create-token--{token}").into(),
94 method: "window/workDoneProgress/create".into(),
95 params: serde_json::to_value(params).expect("WorkDoneProgressCreateParams json"),
96 };
97 connection
98 .sender
99 .send(lsp_server::Message::Request(request))
100 .expect("WorkDoneProgressCreate");
101}