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