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_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
89 .collect::<Vec<_>>();
90 language_server_for_buffers(project.clone(), name.clone(), buffers, cx)
91 })
92 .collect::<FuturesUnordered<_>>();
93
94 cx.spawn(async move |cx| {
95 let mut lsp_tasks = Vec::new();
96 let lsp_task_context = TaskContext::default();
97 while let Some(server_to_query) = lsp_task_sources.next().await {
98 if let Some((server_id, buffers)) = server_to_query {
99 let source_kind = TaskSourceKind::Lsp(server_id);
100 let id_base = source_kind.to_id_base();
101 let mut new_lsp_tasks = Vec::new();
102 for buffer in buffers {
103 if let Ok(runnables_task) = project.update(cx, |project, cx| {
104 let buffer_id = buffer.read(cx).remote_id();
105 project.request_lsp(
106 buffer,
107 LanguageServerToQuery::Other(server_id),
108 GetLspRunnables {
109 buffer_id,
110 position: for_position,
111 },
112 cx,
113 )
114 }) {
115 if let Some(new_runnables) = runnables_task.await.log_err() {
116 new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
117 |(location, runnable)| {
118 let resolved_task =
119 runnable.resolve_task(&id_base, &lsp_task_context)?;
120 Some((location, resolved_task))
121 },
122 ));
123 }
124 }
125 }
126 lsp_tasks.push((source_kind, new_lsp_tasks));
127 }
128 }
129 lsp_tasks
130 })
131}
132
133fn language_server_for_buffers(
134 project: Entity<Project>,
135 name: LanguageServerName,
136 candidates: Vec<Entity<Buffer>>,
137 cx: &mut App,
138) -> Task<Option<(LanguageServerId, Vec<Entity<Buffer>>)>> {
139 cx.spawn(async move |cx| {
140 for buffer in &candidates {
141 let server_id = buffer
142 .update(cx, |buffer, cx| {
143 project.update(cx, |project, cx| {
144 project.language_server_id_for_name(buffer, &name.0, cx)
145 })
146 })
147 .ok()?
148 .await;
149 if let Some(server_id) = server_id {
150 return Some((server_id, candidates));
151 }
152 }
153 None
154 })
155}