1use std::sync::Arc;
2
3use anyhow::Context as _;
4use gpui::{AppContext, Task, ViewContext};
5use language::Language;
6use multi_buffer::MultiBuffer;
7use project::lsp_ext_command::ExpandMacro;
8use text::ToPointUtf16;
9
10use crate::{Editor, ExpandMacroRecursively};
11
12pub fn apply_related_actions(cx: &mut AppContext) {
13 cx.add_async_action(expand_macro_recursively);
14}
15
16pub fn expand_macro_recursively(
17 editor: &mut Editor,
18 _: &ExpandMacroRecursively,
19 cx: &mut ViewContext<'_, '_, Editor>,
20) -> Option<Task<anyhow::Result<()>>> {
21 if editor.selections.count() == 0 {
22 return None;
23 }
24 let project = editor.project.as_ref()?;
25 let workspace = editor.workspace(cx)?;
26 let multibuffer = editor.buffer().read(cx);
27
28 let (trigger_anchor, rust_language, server_to_query, buffer) = editor
29 .selections
30 .disjoint_anchors()
31 .into_iter()
32 .filter(|selection| selection.start == selection.end)
33 .filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
34 .filter_map(|(buffer_id, trigger_anchor)| {
35 let buffer = multibuffer.buffer(buffer_id)?;
36 let rust_language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
37 if !is_rust_language(&rust_language) {
38 return None;
39 }
40 Some((trigger_anchor, rust_language, buffer))
41 })
42 .find_map(|(trigger_anchor, rust_language, buffer)| {
43 project
44 .read(cx)
45 .language_servers_for_buffer(buffer.read(cx), cx)
46 .into_iter()
47 .find_map(|(adapter, server)| {
48 if adapter.name.0.as_ref() == "rust-analyzer" {
49 Some((
50 trigger_anchor,
51 Arc::clone(&rust_language),
52 server.server_id(),
53 buffer.clone(),
54 ))
55 } else {
56 None
57 }
58 })
59 })?;
60
61 let project = project.clone();
62 let buffer_snapshot = buffer.read(cx).snapshot();
63 let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
64 let expand_macro_task = project.update(cx, |project, cx| {
65 project.request_lsp(
66 buffer,
67 project::LanguageServerToQuery::Other(server_to_query),
68 ExpandMacro { position },
69 cx,
70 )
71 });
72 Some(cx.spawn(|_, mut cx| async move {
73 let macro_expansion = expand_macro_task.await.context("expand macro")?;
74 if macro_expansion.is_empty() {
75 log::info!("Empty macro expansion for position {position:?}");
76 return Ok(());
77 }
78
79 let buffer = project.update(&mut cx, |project, cx| {
80 project.create_buffer(¯o_expansion.expansion, Some(rust_language), cx)
81 })?;
82 workspace.update(&mut cx, |workspace, cx| {
83 let buffer = cx.add_model(|cx| {
84 MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
85 });
86 workspace.add_item(
87 Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
88 cx,
89 );
90 });
91
92 anyhow::Ok(())
93 }))
94}
95
96fn is_rust_language(language: &Language) -> bool {
97 language.name().as_ref() == "Rust"
98}