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