1use crate::{
2 LocationLink,
3 lsp_command::{
4 LspCommand, file_path_to_lsp_url, location_link_from_lsp, location_link_from_proto,
5 location_link_to_proto, location_links_from_lsp, location_links_from_proto,
6 location_links_to_proto,
7 },
8 lsp_store::LspStore,
9 make_lsp_text_document_position, make_text_document_identifier,
10};
11use anyhow::{Context as _, Result};
12use async_trait::async_trait;
13use collections::HashMap;
14use gpui::{App, AsyncApp, Entity};
15use language::{
16 Buffer, point_to_lsp,
17 proto::{deserialize_anchor, serialize_anchor},
18};
19use lsp::{AdapterServerCapabilities, LanguageServer, LanguageServerId};
20use rpc::proto::{self, PeerId};
21use serde::{Deserialize, Serialize};
22use std::{
23 path::{Path, PathBuf},
24 sync::Arc,
25};
26use task::{ShellKind, TaskTemplate};
27use text::{BufferId, PointUtf16, ToPointUtf16};
28
29pub enum LspExtExpandMacro {}
30
31impl lsp::request::Request for LspExtExpandMacro {
32 type Params = ExpandMacroParams;
33 type Result = Option<ExpandedMacro>;
34 const METHOD: &'static str = "rust-analyzer/expandMacro";
35}
36
37#[derive(Deserialize, Serialize, Debug)]
38#[serde(rename_all = "camelCase")]
39pub struct ExpandMacroParams {
40 pub text_document: lsp::TextDocumentIdentifier,
41 pub position: lsp::Position,
42}
43
44#[derive(Default, Deserialize, Serialize, Debug)]
45#[serde(rename_all = "camelCase")]
46pub struct ExpandedMacro {
47 pub name: String,
48 pub expansion: String,
49}
50
51impl ExpandedMacro {
52 pub fn is_empty(&self) -> bool {
53 self.name.is_empty() && self.expansion.is_empty()
54 }
55}
56#[derive(Debug)]
57pub struct ExpandMacro {
58 pub position: PointUtf16,
59}
60
61#[async_trait(?Send)]
62impl LspCommand for ExpandMacro {
63 type Response = ExpandedMacro;
64 type LspRequest = LspExtExpandMacro;
65 type ProtoRequest = proto::LspExtExpandMacro;
66
67 fn display_name(&self) -> &str {
68 "Expand macro"
69 }
70
71 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
72 true
73 }
74
75 fn to_lsp(
76 &self,
77 path: &Path,
78 _: &Buffer,
79 _: &Arc<LanguageServer>,
80 _: &App,
81 ) -> Result<ExpandMacroParams> {
82 Ok(ExpandMacroParams {
83 text_document: make_text_document_identifier(path)?,
84 position: point_to_lsp(self.position),
85 })
86 }
87
88 async fn response_from_lsp(
89 self,
90 message: Option<ExpandedMacro>,
91 _: Entity<LspStore>,
92 _: Entity<Buffer>,
93 _: LanguageServerId,
94 _: AsyncApp,
95 ) -> anyhow::Result<ExpandedMacro> {
96 Ok(message
97 .map(|message| ExpandedMacro {
98 name: message.name,
99 expansion: message.expansion,
100 })
101 .unwrap_or_default())
102 }
103
104 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro {
105 proto::LspExtExpandMacro {
106 project_id,
107 buffer_id: buffer.remote_id().into(),
108 position: Some(language::proto::serialize_anchor(
109 &buffer.anchor_before(self.position),
110 )),
111 }
112 }
113
114 async fn from_proto(
115 message: Self::ProtoRequest,
116 _: Entity<LspStore>,
117 buffer: Entity<Buffer>,
118 cx: AsyncApp,
119 ) -> anyhow::Result<Self> {
120 let position = message
121 .position
122 .and_then(deserialize_anchor)
123 .context("invalid position")?;
124 Ok(Self {
125 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
126 })
127 }
128
129 fn response_to_proto(
130 response: ExpandedMacro,
131 _: &mut LspStore,
132 _: PeerId,
133 _: &clock::Global,
134 _: &mut App,
135 ) -> proto::LspExtExpandMacroResponse {
136 proto::LspExtExpandMacroResponse {
137 name: response.name,
138 expansion: response.expansion,
139 }
140 }
141
142 async fn response_from_proto(
143 self,
144 message: proto::LspExtExpandMacroResponse,
145 _: Entity<LspStore>,
146 _: Entity<Buffer>,
147 _: AsyncApp,
148 ) -> anyhow::Result<ExpandedMacro> {
149 Ok(ExpandedMacro {
150 name: message.name,
151 expansion: message.expansion,
152 })
153 }
154
155 fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> Result<BufferId> {
156 BufferId::new(message.buffer_id)
157 }
158}
159
160pub enum LspOpenDocs {}
161
162impl lsp::request::Request for LspOpenDocs {
163 type Params = OpenDocsParams;
164 type Result = Option<DocsUrls>;
165 const METHOD: &'static str = "experimental/externalDocs";
166}
167
168#[derive(Serialize, Deserialize, Debug)]
169#[serde(rename_all = "camelCase")]
170pub struct OpenDocsParams {
171 pub text_document: lsp::TextDocumentIdentifier,
172 pub position: lsp::Position,
173}
174
175#[derive(Serialize, Deserialize, Debug, Default)]
176#[serde(rename_all = "camelCase")]
177pub struct DocsUrls {
178 pub web: Option<String>,
179 pub local: Option<String>,
180}
181
182impl DocsUrls {
183 pub fn is_empty(&self) -> bool {
184 self.web.is_none() && self.local.is_none()
185 }
186}
187
188#[derive(Debug)]
189pub struct OpenDocs {
190 pub position: PointUtf16,
191}
192
193#[async_trait(?Send)]
194impl LspCommand for OpenDocs {
195 type Response = DocsUrls;
196 type LspRequest = LspOpenDocs;
197 type ProtoRequest = proto::LspExtOpenDocs;
198
199 fn display_name(&self) -> &str {
200 "Open docs"
201 }
202
203 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
204 true
205 }
206
207 fn to_lsp(
208 &self,
209 path: &Path,
210 _: &Buffer,
211 _: &Arc<LanguageServer>,
212 _: &App,
213 ) -> Result<OpenDocsParams> {
214 Ok(OpenDocsParams {
215 text_document: lsp::TextDocumentIdentifier {
216 uri: lsp::Uri::from_file_path(path).unwrap(),
217 },
218 position: point_to_lsp(self.position),
219 })
220 }
221
222 async fn response_from_lsp(
223 self,
224 message: Option<DocsUrls>,
225 _: Entity<LspStore>,
226 _: Entity<Buffer>,
227 _: LanguageServerId,
228 _: AsyncApp,
229 ) -> anyhow::Result<DocsUrls> {
230 Ok(message
231 .map(|message| DocsUrls {
232 web: message.web,
233 local: message.local,
234 })
235 .unwrap_or_default())
236 }
237
238 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtOpenDocs {
239 proto::LspExtOpenDocs {
240 project_id,
241 buffer_id: buffer.remote_id().into(),
242 position: Some(language::proto::serialize_anchor(
243 &buffer.anchor_before(self.position),
244 )),
245 }
246 }
247
248 async fn from_proto(
249 message: Self::ProtoRequest,
250 _: Entity<LspStore>,
251 buffer: Entity<Buffer>,
252 cx: AsyncApp,
253 ) -> anyhow::Result<Self> {
254 let position = message
255 .position
256 .and_then(deserialize_anchor)
257 .context("invalid position")?;
258 Ok(Self {
259 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
260 })
261 }
262
263 fn response_to_proto(
264 response: DocsUrls,
265 _: &mut LspStore,
266 _: PeerId,
267 _: &clock::Global,
268 _: &mut App,
269 ) -> proto::LspExtOpenDocsResponse {
270 proto::LspExtOpenDocsResponse {
271 web: response.web,
272 local: response.local,
273 }
274 }
275
276 async fn response_from_proto(
277 self,
278 message: proto::LspExtOpenDocsResponse,
279 _: Entity<LspStore>,
280 _: Entity<Buffer>,
281 _: AsyncApp,
282 ) -> anyhow::Result<DocsUrls> {
283 Ok(DocsUrls {
284 web: message.web,
285 local: message.local,
286 })
287 }
288
289 fn buffer_id_from_proto(message: &proto::LspExtOpenDocs) -> Result<BufferId> {
290 BufferId::new(message.buffer_id)
291 }
292}
293
294pub enum LspSwitchSourceHeader {}
295
296impl lsp::request::Request for LspSwitchSourceHeader {
297 type Params = SwitchSourceHeaderParams;
298 type Result = Option<SwitchSourceHeaderResult>;
299 const METHOD: &'static str = "textDocument/switchSourceHeader";
300}
301
302#[derive(Serialize, Deserialize, Debug)]
303#[serde(rename_all = "camelCase")]
304pub struct SwitchSourceHeaderParams(lsp::TextDocumentIdentifier);
305
306#[derive(Serialize, Deserialize, Debug, Default)]
307#[serde(rename_all = "camelCase")]
308pub struct SwitchSourceHeaderResult(pub String);
309
310#[derive(Default, Deserialize, Serialize, Debug)]
311#[serde(rename_all = "camelCase")]
312pub struct SwitchSourceHeader;
313
314#[derive(Debug)]
315pub struct GoToParentModule {
316 pub position: PointUtf16,
317}
318
319pub struct LspGoToParentModule {}
320
321impl lsp::request::Request for LspGoToParentModule {
322 type Params = lsp::TextDocumentPositionParams;
323 type Result = Option<Vec<lsp::LocationLink>>;
324 const METHOD: &'static str = "experimental/parentModule";
325}
326
327#[async_trait(?Send)]
328impl LspCommand for SwitchSourceHeader {
329 type Response = SwitchSourceHeaderResult;
330 type LspRequest = LspSwitchSourceHeader;
331 type ProtoRequest = proto::LspExtSwitchSourceHeader;
332
333 fn display_name(&self) -> &str {
334 "Switch source header"
335 }
336
337 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
338 true
339 }
340
341 fn to_lsp(
342 &self,
343 path: &Path,
344 _: &Buffer,
345 _: &Arc<LanguageServer>,
346 _: &App,
347 ) -> Result<SwitchSourceHeaderParams> {
348 Ok(SwitchSourceHeaderParams(make_text_document_identifier(
349 path,
350 )?))
351 }
352
353 async fn response_from_lsp(
354 self,
355 message: Option<SwitchSourceHeaderResult>,
356 _: Entity<LspStore>,
357 _: Entity<Buffer>,
358 _: LanguageServerId,
359 _: AsyncApp,
360 ) -> anyhow::Result<SwitchSourceHeaderResult> {
361 Ok(message
362 .map(|message| SwitchSourceHeaderResult(message.0))
363 .unwrap_or_default())
364 }
365
366 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtSwitchSourceHeader {
367 proto::LspExtSwitchSourceHeader {
368 project_id,
369 buffer_id: buffer.remote_id().into(),
370 }
371 }
372
373 async fn from_proto(
374 _: Self::ProtoRequest,
375 _: Entity<LspStore>,
376 _: Entity<Buffer>,
377 _: AsyncApp,
378 ) -> anyhow::Result<Self> {
379 Ok(Self {})
380 }
381
382 fn response_to_proto(
383 response: SwitchSourceHeaderResult,
384 _: &mut LspStore,
385 _: PeerId,
386 _: &clock::Global,
387 _: &mut App,
388 ) -> proto::LspExtSwitchSourceHeaderResponse {
389 proto::LspExtSwitchSourceHeaderResponse {
390 target_file: response.0,
391 }
392 }
393
394 async fn response_from_proto(
395 self,
396 message: proto::LspExtSwitchSourceHeaderResponse,
397 _: Entity<LspStore>,
398 _: Entity<Buffer>,
399 _: AsyncApp,
400 ) -> anyhow::Result<SwitchSourceHeaderResult> {
401 Ok(SwitchSourceHeaderResult(message.target_file))
402 }
403
404 fn buffer_id_from_proto(message: &proto::LspExtSwitchSourceHeader) -> Result<BufferId> {
405 BufferId::new(message.buffer_id)
406 }
407}
408
409#[async_trait(?Send)]
410impl LspCommand for GoToParentModule {
411 type Response = Vec<LocationLink>;
412 type LspRequest = LspGoToParentModule;
413 type ProtoRequest = proto::LspExtGoToParentModule;
414
415 fn display_name(&self) -> &str {
416 "Go to parent module"
417 }
418
419 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
420 true
421 }
422
423 fn to_lsp(
424 &self,
425 path: &Path,
426 _: &Buffer,
427 _: &Arc<LanguageServer>,
428 _: &App,
429 ) -> Result<lsp::TextDocumentPositionParams> {
430 make_lsp_text_document_position(path, self.position)
431 }
432
433 async fn response_from_lsp(
434 self,
435 links: Option<Vec<lsp::LocationLink>>,
436 lsp_store: Entity<LspStore>,
437 buffer: Entity<Buffer>,
438 server_id: LanguageServerId,
439 cx: AsyncApp,
440 ) -> anyhow::Result<Vec<LocationLink>> {
441 location_links_from_lsp(
442 links.map(lsp::GotoDefinitionResponse::Link),
443 lsp_store,
444 buffer,
445 server_id,
446 cx,
447 )
448 .await
449 }
450
451 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtGoToParentModule {
452 proto::LspExtGoToParentModule {
453 project_id,
454 buffer_id: buffer.remote_id().to_proto(),
455 position: Some(language::proto::serialize_anchor(
456 &buffer.anchor_before(self.position),
457 )),
458 }
459 }
460
461 async fn from_proto(
462 request: Self::ProtoRequest,
463 _: Entity<LspStore>,
464 buffer: Entity<Buffer>,
465 cx: AsyncApp,
466 ) -> anyhow::Result<Self> {
467 let position = request
468 .position
469 .and_then(deserialize_anchor)
470 .context("bad request with bad position")?;
471 Ok(Self {
472 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
473 })
474 }
475
476 fn response_to_proto(
477 links: Vec<LocationLink>,
478 lsp_store: &mut LspStore,
479 peer_id: PeerId,
480 _: &clock::Global,
481 cx: &mut App,
482 ) -> proto::LspExtGoToParentModuleResponse {
483 proto::LspExtGoToParentModuleResponse {
484 links: location_links_to_proto(links, lsp_store, peer_id, cx),
485 }
486 }
487
488 async fn response_from_proto(
489 self,
490 message: proto::LspExtGoToParentModuleResponse,
491 lsp_store: Entity<LspStore>,
492 _: Entity<Buffer>,
493 cx: AsyncApp,
494 ) -> anyhow::Result<Vec<LocationLink>> {
495 location_links_from_proto(message.links, lsp_store, cx).await
496 }
497
498 fn buffer_id_from_proto(message: &proto::LspExtGoToParentModule) -> Result<BufferId> {
499 BufferId::new(message.buffer_id)
500 }
501}
502
503// https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#runnables
504// Taken from https://github.com/rust-lang/rust-analyzer/blob/a73a37a757a58b43a796d3eb86a1f7dfd0036659/crates/rust-analyzer/src/lsp/ext.rs#L425-L489
505pub enum Runnables {}
506
507impl lsp::request::Request for Runnables {
508 type Params = RunnablesParams;
509 type Result = Vec<Runnable>;
510 const METHOD: &'static str = "experimental/runnables";
511}
512
513#[derive(Serialize, Deserialize, Debug, Clone)]
514#[serde(rename_all = "camelCase")]
515pub struct RunnablesParams {
516 pub text_document: lsp::TextDocumentIdentifier,
517 #[serde(default)]
518 pub position: Option<lsp::Position>,
519}
520
521#[derive(Deserialize, Serialize, Debug, Clone)]
522#[serde(rename_all = "camelCase")]
523pub struct Runnable {
524 pub label: String,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
526 pub location: Option<lsp::LocationLink>,
527 pub kind: RunnableKind,
528 pub args: RunnableArgs,
529}
530
531#[derive(Deserialize, Serialize, Debug, Clone)]
532#[serde(rename_all = "camelCase")]
533#[serde(untagged)]
534pub enum RunnableArgs {
535 Cargo(CargoRunnableArgs),
536 Shell(ShellRunnableArgs),
537}
538
539#[derive(Serialize, Deserialize, Debug, Clone)]
540#[serde(rename_all = "lowercase")]
541pub enum RunnableKind {
542 Cargo,
543 Shell,
544}
545
546#[derive(Deserialize, Serialize, Debug, Clone)]
547#[serde(rename_all = "camelCase")]
548pub struct CargoRunnableArgs {
549 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
550 pub environment: HashMap<String, String>,
551 pub cwd: PathBuf,
552 /// Command to be executed instead of cargo
553 #[serde(default)]
554 pub override_cargo: Option<String>,
555 #[serde(default, skip_serializing_if = "Option::is_none")]
556 pub workspace_root: Option<PathBuf>,
557 // command, --package and --lib stuff
558 #[serde(default)]
559 pub cargo_args: Vec<String>,
560 // stuff after --
561 #[serde(default)]
562 pub executable_args: Vec<String>,
563}
564
565#[derive(Deserialize, Serialize, Debug, Clone)]
566#[serde(rename_all = "camelCase")]
567pub struct ShellRunnableArgs {
568 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
569 pub environment: HashMap<String, String>,
570 pub cwd: PathBuf,
571 pub program: String,
572 #[serde(default)]
573 pub args: Vec<String>,
574}
575
576#[derive(Debug)]
577pub struct GetLspRunnables {
578 pub buffer_id: BufferId,
579 pub position: Option<text::Anchor>,
580}
581
582#[derive(Debug, Default)]
583pub struct LspRunnables {
584 pub runnables: Vec<(Option<LocationLink>, TaskTemplate)>,
585}
586
587#[async_trait(?Send)]
588impl LspCommand for GetLspRunnables {
589 type Response = LspRunnables;
590 type LspRequest = Runnables;
591 type ProtoRequest = proto::LspExtRunnables;
592
593 fn display_name(&self) -> &str {
594 "LSP Runnables"
595 }
596
597 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
598 true
599 }
600
601 fn to_lsp(
602 &self,
603 path: &Path,
604 buffer: &Buffer,
605 _: &Arc<LanguageServer>,
606 _: &App,
607 ) -> Result<RunnablesParams> {
608 let url = file_path_to_lsp_url(path)?;
609 Ok(RunnablesParams {
610 text_document: lsp::TextDocumentIdentifier::new(url),
611 position: self
612 .position
613 .map(|anchor| point_to_lsp(anchor.to_point_utf16(&buffer.snapshot()))),
614 })
615 }
616
617 async fn response_from_lsp(
618 self,
619 lsp_runnables: Vec<Runnable>,
620 lsp_store: Entity<LspStore>,
621 buffer: Entity<Buffer>,
622 server_id: LanguageServerId,
623 mut cx: AsyncApp,
624 ) -> Result<LspRunnables> {
625 let mut runnables = Vec::with_capacity(lsp_runnables.len());
626
627 for runnable in lsp_runnables {
628 let location = match runnable.location {
629 Some(location) => Some(
630 location_link_from_lsp(location, &lsp_store, &buffer, server_id, &mut cx)
631 .await?,
632 ),
633 None => None,
634 };
635 let mut task_template = TaskTemplate::default();
636 task_template.label = runnable.label;
637 match runnable.args {
638 RunnableArgs::Cargo(cargo) => {
639 match cargo.override_cargo {
640 Some(override_cargo) => {
641 let mut override_parts =
642 override_cargo.split(" ").map(|s| s.to_string());
643 task_template.command = override_parts
644 .next()
645 .unwrap_or_else(|| override_cargo.clone());
646 task_template.args.extend(override_parts);
647 }
648 None => task_template.command = "cargo".to_string(),
649 };
650 task_template.env = cargo.environment;
651 task_template.cwd = Some(
652 cargo
653 .workspace_root
654 .unwrap_or(cargo.cwd)
655 .to_string_lossy()
656 .to_string(),
657 );
658 task_template.args.extend(cargo.cargo_args);
659 if !cargo.executable_args.is_empty() {
660 let shell_kind = task_template
661 .shell
662 .as_ref()
663 .map_or_else(ShellKind::system, |shell| {
664 shell.shell_kind(cfg!(windows))
665 });
666 task_template.args.push("--".to_string());
667 task_template.args.extend(
668 cargo
669 .executable_args
670 .into_iter()
671 // rust-analyzer's doctest data may be smth. like
672 // ```
673 // command: "cargo",
674 // args: [
675 // "test",
676 // "--doc",
677 // "--package",
678 // "cargo-output-parser",
679 // "--",
680 // "X<T>::new",
681 // "--show-output",
682 // ],
683 // ```
684 // and `X<T>::new` will cause troubles if not escaped properly, as later
685 // the task runs as `$SHELL -i -c "cargo test ..."`.
686 //
687 // We cannot escape all shell arguments unconditionally, as we use this for ssh commands, which may involve paths starting with `~`.
688 // That bit is not auto-expanded when using single quotes.
689 // Escape extra cargo args unconditionally as those are unlikely to contain `~`.
690 .flat_map(|extra_arg| {
691 shell_kind.try_quote(&extra_arg).map(|s| s.to_string())
692 }),
693 );
694 }
695 }
696 RunnableArgs::Shell(shell) => {
697 task_template.command = shell.program;
698 task_template.args = shell.args;
699 task_template.env = shell.environment;
700 task_template.cwd = Some(shell.cwd.to_string_lossy().into_owned());
701 }
702 }
703
704 runnables.push((location, task_template));
705 }
706
707 Ok(LspRunnables { runnables })
708 }
709
710 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtRunnables {
711 proto::LspExtRunnables {
712 project_id,
713 buffer_id: buffer.remote_id().to_proto(),
714 position: self.position.as_ref().map(serialize_anchor),
715 }
716 }
717
718 async fn from_proto(
719 message: proto::LspExtRunnables,
720 _: Entity<LspStore>,
721 _: Entity<Buffer>,
722 _: AsyncApp,
723 ) -> Result<Self> {
724 let buffer_id = Self::buffer_id_from_proto(&message)?;
725 let position = message.position.and_then(deserialize_anchor);
726 Ok(Self {
727 buffer_id,
728 position,
729 })
730 }
731
732 fn response_to_proto(
733 response: LspRunnables,
734 lsp_store: &mut LspStore,
735 peer_id: PeerId,
736 _: &clock::Global,
737 cx: &mut App,
738 ) -> proto::LspExtRunnablesResponse {
739 proto::LspExtRunnablesResponse {
740 runnables: response
741 .runnables
742 .into_iter()
743 .map(|(location, task_template)| proto::LspRunnable {
744 location: location
745 .map(|location| location_link_to_proto(location, lsp_store, peer_id, cx)),
746 task_template: serde_json::to_vec(&task_template).unwrap(),
747 })
748 .collect(),
749 }
750 }
751
752 async fn response_from_proto(
753 self,
754 message: proto::LspExtRunnablesResponse,
755 lsp_store: Entity<LspStore>,
756 _: Entity<Buffer>,
757 mut cx: AsyncApp,
758 ) -> Result<LspRunnables> {
759 let mut runnables = LspRunnables {
760 runnables: Vec::new(),
761 };
762
763 for lsp_runnable in message.runnables {
764 let location = match lsp_runnable.location {
765 Some(location) => {
766 Some(location_link_from_proto(location, lsp_store.clone(), &mut cx).await?)
767 }
768 None => None,
769 };
770 let task_template = serde_json::from_slice(&lsp_runnable.task_template)
771 .context("deserializing task template from proto")?;
772 runnables.runnables.push((location, task_template));
773 }
774
775 Ok(runnables)
776 }
777
778 fn buffer_id_from_proto(message: &proto::LspExtRunnables) -> Result<BufferId> {
779 BufferId::new(message.buffer_id)
780 }
781}
782
783#[derive(Debug)]
784pub struct LspExtCancelFlycheck {}
785
786#[derive(Debug)]
787pub struct LspExtRunFlycheck {}
788
789#[derive(Debug)]
790pub struct LspExtClearFlycheck {}
791
792impl lsp::notification::Notification for LspExtCancelFlycheck {
793 type Params = ();
794 const METHOD: &'static str = "rust-analyzer/cancelFlycheck";
795}
796
797impl lsp::notification::Notification for LspExtRunFlycheck {
798 type Params = RunFlycheckParams;
799 const METHOD: &'static str = "rust-analyzer/runFlycheck";
800}
801
802#[derive(Deserialize, Serialize, Debug)]
803#[serde(rename_all = "camelCase")]
804pub struct RunFlycheckParams {
805 pub text_document: Option<lsp::TextDocumentIdentifier>,
806}
807
808impl lsp::notification::Notification for LspExtClearFlycheck {
809 type Params = ();
810 const METHOD: &'static str = "rust-analyzer/clearFlycheck";
811}