1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::Editor;
5use collections::HashMap;
6use gpui::AsyncApp;
7use gpui::{App, Entity, Task};
8use itertools::Itertools;
9use language::Buffer;
10use language::Language;
11use lsp::LanguageServerId;
12use lsp::LanguageServerName;
13use multi_buffer::Anchor;
14use project::LanguageServerToQuery;
15use project::LocationLink;
16use project::Project;
17use project::TaskSourceKind;
18use project::lsp_store::lsp_ext_command::GetLspRunnables;
19use smol::future::FutureExt as _;
20use task::ResolvedTask;
21use task::TaskContext;
22use text::BufferId;
23use ui::SharedString;
24use util::{ResultExt as _, maybe};
25
26pub(crate) fn find_specific_language_server_in_selection<F>(
27 editor: &Editor,
28 cx: &mut App,
29 filter_language: F,
30 language_server_name: LanguageServerName,
31) -> Option<(Anchor, Arc<Language>, LanguageServerId, Entity<Buffer>)>
32where
33 F: Fn(&Language) -> bool,
34{
35 let project = editor.project.clone()?;
36 editor
37 .selections
38 .disjoint_anchors_arc()
39 .iter()
40 .filter_map(|selection| Some((selection.head(), selection.head().buffer_id?)))
41 .unique_by(|(_, buffer_id)| *buffer_id)
42 .find_map(|(trigger_anchor, buffer_id)| {
43 let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
44 let language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
45 if filter_language(&language) {
46 let server_id = buffer.update(cx, |buffer, cx| {
47 project
48 .read(cx)
49 .language_server_id_for_name(buffer, &language_server_name, cx)
50 })?;
51 Some((trigger_anchor, language, server_id, buffer))
52 } else {
53 None
54 }
55 })
56}
57
58async fn lsp_task_context(
59 project: &Entity<Project>,
60 buffer: &Entity<Buffer>,
61 cx: &mut AsyncApp,
62) -> Option<TaskContext> {
63 let worktree_store = project
64 .read_with(cx, |project, _| project.worktree_store())
65 .ok()?;
66
67 let worktree_abs_path = cx
68 .update(|cx| {
69 let worktree_id = buffer.read(cx).file().map(|f| f.worktree_id(cx));
70
71 worktree_id
72 .and_then(|worktree_id| worktree_store.read(cx).worktree_for_id(worktree_id, cx))
73 .and_then(|worktree| worktree.read(cx).root_dir())
74 })
75 .ok()?;
76
77 let project_env = project
78 .update(cx, |project, cx| {
79 project.buffer_environment(buffer, &worktree_store, cx)
80 })
81 .ok()?
82 .await;
83
84 Some(TaskContext {
85 cwd: worktree_abs_path.map(|p| p.to_path_buf()),
86 project_env: project_env.unwrap_or_default(),
87 ..TaskContext::default()
88 })
89}
90
91pub fn lsp_tasks(
92 project: Entity<Project>,
93 task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
94 for_position: Option<text::Anchor>,
95 cx: &mut App,
96) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
97 let lsp_task_sources = task_sources
98 .iter()
99 .filter_map(|(name, buffer_ids)| {
100 let buffers = buffer_ids
101 .iter()
102 .filter(|&&buffer_id| match for_position {
103 Some(for_position) => for_position.buffer_id == Some(buffer_id),
104 None => true,
105 })
106 .filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
107 .collect::<Vec<_>>();
108
109 let server_id = buffers.iter().find_map(|buffer| {
110 project.read_with(cx, |project, cx| {
111 project.language_server_id_for_name(buffer.read(cx), name, cx)
112 })
113 });
114 server_id.zip(Some(buffers))
115 })
116 .collect::<Vec<_>>();
117 let remote_shell = maybe!({ project.read(cx).remote_client()?.read(cx).shell() });
118 cx.spawn(async move |cx| {
119 cx.spawn(async move |cx| {
120 let mut lsp_tasks = HashMap::default();
121 for (server_id, buffers) in lsp_task_sources {
122 let mut new_lsp_tasks = Vec::new();
123 for buffer in buffers {
124 let source_kind = match buffer.update(cx, |buffer, _| {
125 buffer.language().map(|language| language.name())
126 }) {
127 Ok(Some(language_name)) => TaskSourceKind::Lsp {
128 server: server_id,
129 language_name: SharedString::from(language_name),
130 },
131 Ok(None) => continue,
132 Err(_) => return Vec::new(),
133 };
134 let id_base = source_kind.to_id_base();
135 let lsp_buffer_context = lsp_task_context(&project, &buffer, cx)
136 .await
137 .unwrap_or_default();
138
139 if let Ok(runnables_task) = project.update(cx, |project, cx| {
140 let buffer_id = buffer.read(cx).remote_id();
141 project.request_lsp(
142 buffer,
143 LanguageServerToQuery::Other(server_id),
144 GetLspRunnables {
145 buffer_id,
146 position: for_position,
147 },
148 cx,
149 )
150 }) && let Some(new_runnables) = runnables_task.await.log_err()
151 {
152 new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
153 |(location, runnable)| {
154 let resolved_task = runnable.resolve_task(
155 &id_base,
156 &|| remote_shell.clone(),
157 &lsp_buffer_context,
158 )?;
159 Some((location, resolved_task))
160 },
161 ));
162 }
163 lsp_tasks
164 .entry(source_kind)
165 .or_insert_with(Vec::new)
166 .append(&mut new_lsp_tasks);
167 }
168 }
169 lsp_tasks.into_iter().collect()
170 })
171 .race({
172 // `lsp::LSP_REQUEST_TIMEOUT` is larger than we want for the modal to open fast
173 let timer = cx.background_executor().timer(Duration::from_millis(200));
174 async move {
175 timer.await;
176 log::info!("Timed out waiting for LSP tasks");
177 Vec::new()
178 }
179 })
180 .await
181 })
182}