clangd_ext.rs

 1use anyhow::Context as _;
 2use gpui::{App, Context, Entity, Window};
 3use language::Language;
 4use url::Url;
 5
 6use crate::lsp_ext::find_specific_language_server_in_selection;
 7
 8use crate::{element::register_action, Editor, SwitchSourceHeader};
 9
10const CLANGD_SERVER_NAME: &str = "clangd";
11
12fn is_c_language(language: &Language) -> bool {
13    return language.name() == "C++".into() || language.name() == "C".into();
14}
15
16pub fn switch_source_header(
17    editor: &mut Editor,
18    _: &SwitchSourceHeader,
19    window: &mut Window,
20    cx: &mut Context<Editor>,
21) {
22    let Some(project) = &editor.project else {
23        return;
24    };
25    let Some(workspace) = editor.workspace() else {
26        return;
27    };
28
29    let Some((_, _, server_to_query, buffer)) =
30        find_specific_language_server_in_selection(editor, cx, is_c_language, CLANGD_SERVER_NAME)
31    else {
32        return;
33    };
34
35    let project = project.clone();
36    let buffer_snapshot = buffer.read(cx).snapshot();
37    let source_file = buffer_snapshot
38        .file()
39        .unwrap()
40        .file_name(cx)
41        .to_str()
42        .unwrap()
43        .to_owned();
44
45    let switch_source_header_task = project.update(cx, |project, cx| {
46        project.request_lsp(
47            buffer,
48            project::LanguageServerToQuery::Other(server_to_query),
49            project::lsp_ext_command::SwitchSourceHeader,
50            cx,
51        )
52    });
53    cx.spawn_in(window, |_editor, mut cx| async move {
54        let switch_source_header = switch_source_header_task
55            .await
56            .with_context(|| format!("Switch source/header LSP request for path \"{source_file}\" failed"))?;
57        if switch_source_header.0.is_empty() {
58            log::info!("Clangd returned an empty string when requesting to switch source/header from \"{source_file}\"" );
59            return Ok(());
60        }
61
62        let goto = Url::parse(&switch_source_header.0).with_context(|| {
63            format!(
64                "Parsing URL \"{}\" returned from switch source/header failed",
65                switch_source_header.0
66            )
67        })?;
68
69        let path = goto.to_file_path().map_err(|()| {
70            anyhow::anyhow!("URL conversion to file path failed for \"{goto}\"")
71        })?;
72
73        workspace
74            .update_in(&mut cx, |workspace, window, cx| {
75                workspace.open_abs_path(path, false, window, cx)
76            })
77            .with_context(|| {
78                format!(
79                    "Switch source/header could not open \"{goto}\" in workspace"
80                )
81            })?
82            .await
83            .map(|_| ())
84    })
85    .detach_and_log_err(cx);
86}
87
88pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
89    if editor.update(cx, |e, cx| {
90        find_specific_language_server_in_selection(e, cx, is_c_language, CLANGD_SERVER_NAME)
91            .is_some()
92    }) {
93        register_action(editor, window, switch_source_header);
94    }
95}