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