rust_analyzer_ext.rs

 1use anyhow::Context as _;
 2use gpui::{Context, View, ViewContext, VisualContext, WindowContext};
 3use language::Language;
 4use multi_buffer::MultiBuffer;
 5use project::lsp_ext_command::ExpandMacro;
 6use text::ToPointUtf16;
 7
 8use crate::{
 9    element::register_action, lsp_ext::find_specific_language_server_in_selection, Editor,
10    ExpandMacroRecursively,
11};
12
13const RUST_ANALYZER_NAME: &str = "rust-analyzer";
14
15fn is_rust_language(language: &Language) -> bool {
16    language.name() == "Rust".into()
17}
18
19pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
20    if editor
21        .update(cx, |e, cx| {
22            find_specific_language_server_in_selection(e, cx, is_rust_language, RUST_ANALYZER_NAME)
23        })
24        .is_some()
25    {
26        register_action(editor, cx, expand_macro_recursively);
27    }
28}
29
30pub fn expand_macro_recursively(
31    editor: &mut Editor,
32    _: &ExpandMacroRecursively,
33    cx: &mut ViewContext<'_, Editor>,
34) {
35    if editor.selections.count() == 0 {
36        return;
37    }
38    let Some(project) = &editor.project else {
39        return;
40    };
41    let Some(workspace) = editor.workspace() else {
42        return;
43    };
44
45    let Some((trigger_anchor, rust_language, server_to_query, buffer)) =
46        find_specific_language_server_in_selection(
47            editor,
48            cx,
49            is_rust_language,
50            RUST_ANALYZER_NAME,
51        )
52    else {
53        return;
54    };
55
56    let project = project.clone();
57    let buffer_snapshot = buffer.read(cx).snapshot();
58    let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
59    let expand_macro_task = project.update(cx, |project, cx| {
60        project.request_lsp(
61            buffer,
62            project::LanguageServerToQuery::Other(server_to_query),
63            ExpandMacro { position },
64            cx,
65        )
66    });
67    cx.spawn(|_editor, mut cx| async move {
68        let macro_expansion = expand_macro_task.await.context("expand macro")?;
69        if macro_expansion.is_empty() {
70            log::info!("Empty macro expansion for position {position:?}");
71            return Ok(());
72        }
73
74        let buffer = project
75            .update(&mut cx, |project, cx| project.create_buffer(cx))?
76            .await?;
77        workspace.update(&mut cx, |workspace, cx| {
78            buffer.update(cx, |buffer, cx| {
79                buffer.edit([(0..0, macro_expansion.expansion)], None, cx);
80                buffer.set_language(Some(rust_language), cx)
81            });
82            let multibuffer = cx.new_model(|cx| {
83                MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
84            });
85            workspace.add_item_to_active_pane(
86                Box::new(
87                    cx.new_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), true, cx)),
88                ),
89                None,
90                true,
91                cx,
92            );
93        })
94    })
95    .detach_and_log_err(cx);
96}