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}