1use std::sync::Arc;
2
3use crate::Editor;
4use collections::HashMap;
5use futures::stream::FuturesUnordered;
6use gpui::AsyncApp;
7use gpui::{App, AppContext as _, 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::stream::StreamExt;
20use task::ResolvedTask;
21use task::TaskContext;
22use text::BufferId;
23use util::ResultExt as _;
24
25pub(crate) fn find_specific_language_server_in_selection<F>(
26 editor: &Editor,
27 cx: &mut App,
28 filter_language: F,
29 language_server_name: &str,
30) -> Task<Option<(Anchor, Arc<Language>, LanguageServerId, Entity<Buffer>)>>
31where
32 F: Fn(&Language) -> bool,
33{
34 let Some(project) = &editor.project else {
35 return Task::ready(None);
36 };
37
38 let applicable_buffers = editor
39 .selections
40 .disjoint_anchors()
41 .iter()
42 .filter(|selection| selection.start == selection.end)
43 .filter_map(|selection| Some((selection.start, selection.start.buffer_id?)))
44 .filter_map(|(trigger_anchor, buffer_id)| {
45 let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
46 let language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
47 if filter_language(&language) {
48 Some((trigger_anchor, buffer, language))
49 } else {
50 None
51 }
52 })
53 .unique_by(|(_, buffer, _)| buffer.read(cx).remote_id())
54 .collect::<Vec<_>>();
55
56 let applicable_buffer_tasks = applicable_buffers
57 .into_iter()
58 .map(|(trigger_anchor, buffer, language)| {
59 let task = buffer.update(cx, |buffer, cx| {
60 project.update(cx, |project, cx| {
61 project.language_server_id_for_name(buffer, language_server_name, cx)
62 })
63 });
64 (trigger_anchor, buffer, language, task)
65 })
66 .collect::<Vec<_>>();
67 cx.background_spawn(async move {
68 for (trigger_anchor, buffer, language, task) in applicable_buffer_tasks {
69 if let Some(server_id) = task.await {
70 return Some((trigger_anchor, language, server_id, buffer));
71 }
72 }
73
74 None
75 })
76}
77
78async fn lsp_task_context(
79 project: &Entity<Project>,
80 buffer: &Entity<Buffer>,
81 cx: &mut AsyncApp,
82) -> Option<TaskContext> {
83 let worktree_store = project
84 .read_with(cx, |project, _| project.worktree_store())
85 .ok()?;
86
87 let worktree_abs_path = cx
88 .update(|cx| {
89 let worktree_id = buffer.read(cx).file().map(|f| f.worktree_id(cx));
90
91 worktree_id
92 .and_then(|worktree_id| worktree_store.read(cx).worktree_for_id(worktree_id, cx))
93 .and_then(|worktree| worktree.read(cx).root_dir())
94 })
95 .ok()?;
96
97 let project_env = project
98 .update(cx, |project, cx| {
99 project.buffer_environment(&buffer, &worktree_store, cx)
100 })
101 .ok()?
102 .await;
103
104 Some(TaskContext {
105 cwd: worktree_abs_path.map(|p| p.to_path_buf()),
106 project_env: project_env.unwrap_or_default(),
107 ..TaskContext::default()
108 })
109}
110
111pub fn lsp_tasks(
112 project: Entity<Project>,
113 task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
114 for_position: Option<text::Anchor>,
115 cx: &mut App,
116) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
117 let mut lsp_task_sources = task_sources
118 .iter()
119 .map(|(name, buffer_ids)| {
120 let buffers = buffer_ids
121 .iter()
122 .filter(|&&buffer_id| match for_position {
123 Some(for_position) => for_position.buffer_id == Some(buffer_id),
124 None => true,
125 })
126 .filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
127 .collect::<Vec<_>>();
128 language_server_for_buffers(project.clone(), name.clone(), buffers, cx)
129 })
130 .collect::<FuturesUnordered<_>>();
131
132 cx.spawn(async move |cx| {
133 let mut lsp_tasks = Vec::new();
134 while let Some(server_to_query) = lsp_task_sources.next().await {
135 if let Some((server_id, buffers)) = server_to_query {
136 let source_kind = TaskSourceKind::Lsp(server_id);
137 let id_base = source_kind.to_id_base();
138 let mut new_lsp_tasks = Vec::new();
139 for buffer in buffers {
140 let lsp_buffer_context = lsp_task_context(&project, &buffer, cx)
141 .await
142 .unwrap_or_default();
143
144 if let Ok(runnables_task) = project.update(cx, |project, cx| {
145 let buffer_id = buffer.read(cx).remote_id();
146 project.request_lsp(
147 buffer,
148 LanguageServerToQuery::Other(server_id),
149 GetLspRunnables {
150 buffer_id,
151 position: for_position,
152 },
153 cx,
154 )
155 }) {
156 if let Some(new_runnables) = runnables_task.await.log_err() {
157 new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
158 |(location, runnable)| {
159 let resolved_task =
160 runnable.resolve_task(&id_base, &lsp_buffer_context)?;
161 Some((location, resolved_task))
162 },
163 ));
164 }
165 }
166 }
167 lsp_tasks.push((source_kind, new_lsp_tasks));
168 }
169 }
170 lsp_tasks
171 })
172}
173
174fn language_server_for_buffers(
175 project: Entity<Project>,
176 name: LanguageServerName,
177 candidates: Vec<Entity<Buffer>>,
178 cx: &mut App,
179) -> Task<Option<(LanguageServerId, Vec<Entity<Buffer>>)>> {
180 cx.spawn(async move |cx| {
181 for buffer in &candidates {
182 let server_id = buffer
183 .update(cx, |buffer, cx| {
184 project.update(cx, |project, cx| {
185 project.language_server_id_for_name(buffer, &name.0, cx)
186 })
187 })
188 .ok()?
189 .await;
190 if let Some(server_id) = server_id {
191 return Some((server_id, candidates));
192 }
193 }
194 None
195 })
196}