1use std::sync::Arc;
2
3use crate::Editor;
4use collections::HashMap;
5use futures::stream::FuturesUnordered;
6use gpui::{App, AppContext as _, Entity, Task};
7use itertools::Itertools;
8use language::Buffer;
9use language::Language;
10use lsp::LanguageServerId;
11use lsp::LanguageServerName;
12use multi_buffer::Anchor;
13use project::LanguageServerToQuery;
14use project::LocationLink;
15use project::Project;
16use project::TaskSourceKind;
17use project::lsp_store::lsp_ext_command::GetLspRunnables;
18use smol::stream::StreamExt;
19use task::ResolvedTask;
20use task::TaskContext;
21use text::BufferId;
22use util::ResultExt as _;
23
24pub(crate) fn find_specific_language_server_in_selection<F>(
25 editor: &Editor,
26 cx: &mut App,
27 filter_language: F,
28 language_server_name: &str,
29) -> Task<Option<(Anchor, Arc<Language>, LanguageServerId, Entity<Buffer>)>>
30where
31 F: Fn(&Language) -> bool,
32{
33 let Some(project) = &editor.project else {
34 return Task::ready(None);
35 };
36
37 let applicable_buffers = editor
38 .selections
39 .disjoint_anchors()
40 .iter()
41 .filter(|selection| selection.start == selection.end)
42 .filter_map(|selection| Some((selection.start, selection.start.buffer_id?)))
43 .filter_map(|(trigger_anchor, buffer_id)| {
44 let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
45 let language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
46 if filter_language(&language) {
47 Some((trigger_anchor, buffer, language))
48 } else {
49 None
50 }
51 })
52 .unique_by(|(_, buffer, _)| buffer.read(cx).remote_id())
53 .collect::<Vec<_>>();
54
55 let applicable_buffer_tasks = applicable_buffers
56 .into_iter()
57 .map(|(trigger_anchor, buffer, language)| {
58 let task = buffer.update(cx, |buffer, cx| {
59 project.update(cx, |project, cx| {
60 project.language_server_id_for_name(buffer, language_server_name, cx)
61 })
62 });
63 (trigger_anchor, buffer, language, task)
64 })
65 .collect::<Vec<_>>();
66 cx.background_spawn(async move {
67 for (trigger_anchor, buffer, language, task) in applicable_buffer_tasks {
68 if let Some(server_id) = task.await {
69 return Some((trigger_anchor, language, server_id, buffer));
70 }
71 }
72
73 None
74 })
75}
76
77pub fn lsp_tasks(
78 project: Entity<Project>,
79 task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
80 for_position: Option<text::Anchor>,
81 cx: &mut App,
82) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
83 let mut lsp_task_sources = task_sources
84 .iter()
85 .map(|(name, buffer_ids)| {
86 let buffers = buffer_ids
87 .iter()
88 .filter(|&&buffer_id| match for_position {
89 Some(for_position) => for_position.buffer_id == Some(buffer_id),
90 None => true,
91 })
92 .filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
93 .collect::<Vec<_>>();
94 language_server_for_buffers(project.clone(), name.clone(), buffers, cx)
95 })
96 .collect::<FuturesUnordered<_>>();
97
98 cx.spawn(async move |cx| {
99 let mut lsp_tasks = Vec::new();
100 let lsp_task_context = TaskContext::default();
101 while let Some(server_to_query) = lsp_task_sources.next().await {
102 if let Some((server_id, buffers)) = server_to_query {
103 let source_kind = TaskSourceKind::Lsp(server_id);
104 let id_base = source_kind.to_id_base();
105 let mut new_lsp_tasks = Vec::new();
106 for buffer in buffers {
107 if let Ok(runnables_task) = project.update(cx, |project, cx| {
108 let buffer_id = buffer.read(cx).remote_id();
109 project.request_lsp(
110 buffer,
111 LanguageServerToQuery::Other(server_id),
112 GetLspRunnables {
113 buffer_id,
114 position: for_position,
115 },
116 cx,
117 )
118 }) {
119 if let Some(new_runnables) = runnables_task.await.log_err() {
120 new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
121 |(location, runnable)| {
122 let resolved_task =
123 runnable.resolve_task(&id_base, &lsp_task_context)?;
124 Some((location, resolved_task))
125 },
126 ));
127 }
128 }
129 }
130 lsp_tasks.push((source_kind, new_lsp_tasks));
131 }
132 }
133 lsp_tasks
134 })
135}
136
137fn language_server_for_buffers(
138 project: Entity<Project>,
139 name: LanguageServerName,
140 candidates: Vec<Entity<Buffer>>,
141 cx: &mut App,
142) -> Task<Option<(LanguageServerId, Vec<Entity<Buffer>>)>> {
143 cx.spawn(async move |cx| {
144 for buffer in &candidates {
145 let server_id = buffer
146 .update(cx, |buffer, cx| {
147 project.update(cx, |project, cx| {
148 project.language_server_id_for_name(buffer, &name.0, cx)
149 })
150 })
151 .ok()?
152 .await;
153 if let Some(server_id) = server_id {
154 return Some((server_id, candidates));
155 }
156 }
157 None
158 })
159}