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 _;
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, environment) = project
64 .read_with(cx, |project, _| {
65 (project.worktree_store(), project.environment().clone())
66 })
67 .ok()?;
68
69 let worktree_abs_path = cx
70 .update(|cx| {
71 let worktree_id = buffer.read(cx).file().map(|f| f.worktree_id(cx));
72
73 worktree_id
74 .and_then(|worktree_id| worktree_store.read(cx).worktree_for_id(worktree_id, cx))
75 .and_then(|worktree| worktree.read(cx).root_dir())
76 })
77 .ok()?;
78
79 let project_env = environment
80 .update(cx, |environment, cx| {
81 environment.buffer_environment(buffer, &worktree_store, cx)
82 })
83 .ok()?
84 .await;
85
86 Some(TaskContext {
87 cwd: worktree_abs_path.map(|p| p.to_path_buf()),
88 project_env: project_env.unwrap_or_default(),
89 ..TaskContext::default()
90 })
91}
92
93pub fn lsp_tasks(
94 project: Entity<Project>,
95 task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
96 for_position: Option<text::Anchor>,
97 cx: &mut App,
98) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
99 let lsp_task_sources = task_sources
100 .iter()
101 .filter_map(|(name, buffer_ids)| {
102 let buffers = buffer_ids
103 .iter()
104 .filter(|&&buffer_id| match for_position {
105 Some(for_position) => for_position.buffer_id == Some(buffer_id),
106 None => true,
107 })
108 .filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
109 .collect::<Vec<_>>();
110
111 let server_id = buffers.iter().find_map(|buffer| {
112 project.read_with(cx, |project, cx| {
113 project.language_server_id_for_name(buffer.read(cx), name, cx)
114 })
115 });
116 server_id.zip(Some(buffers))
117 })
118 .collect::<Vec<_>>();
119
120 cx.spawn(async move |cx| {
121 cx.spawn(async move |cx| {
122 let mut lsp_tasks = HashMap::default();
123 for (server_id, buffers) in lsp_task_sources {
124 let mut new_lsp_tasks = Vec::new();
125 for buffer in buffers {
126 let source_kind = match buffer.update(cx, |buffer, _| {
127 buffer.language().map(|language| language.name())
128 }) {
129 Ok(Some(language_name)) => TaskSourceKind::Lsp {
130 server: server_id,
131 language_name: SharedString::from(language_name),
132 },
133 Ok(None) => continue,
134 Err(_) => return Vec::new(),
135 };
136 let id_base = source_kind.to_id_base();
137 let lsp_buffer_context = lsp_task_context(&project, &buffer, cx)
138 .await
139 .unwrap_or_default();
140
141 if let Ok(runnables_task) = project.update(cx, |project, cx| {
142 let buffer_id = buffer.read(cx).remote_id();
143 project.request_lsp(
144 buffer,
145 LanguageServerToQuery::Other(server_id),
146 GetLspRunnables {
147 buffer_id,
148 position: for_position,
149 },
150 cx,
151 )
152 }) && let Some(new_runnables) = runnables_task.await.log_err()
153 {
154 new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
155 |(location, runnable)| {
156 let resolved_task =
157 runnable.resolve_task(&id_base, &lsp_buffer_context)?;
158 Some((location, resolved_task))
159 },
160 ));
161 }
162 lsp_tasks
163 .entry(source_kind)
164 .or_insert_with(Vec::new)
165 .append(&mut new_lsp_tasks);
166 }
167 }
168 lsp_tasks.into_iter().collect()
169 })
170 .race({
171 // `lsp::LSP_REQUEST_TIMEOUT` is larger than we want for the modal to open fast
172 let timer = cx.background_executor().timer(Duration::from_millis(200));
173 async move {
174 timer.await;
175 log::info!("Timed out waiting for LSP tasks");
176 Vec::new()
177 }
178 })
179 .await
180 })
181}