rust_analyzer_ext.rs

 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(&macro_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}