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 = 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
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 =
155                                    runnable.resolve_task(&id_base, &lsp_buffer_context)?;
156                                Some((location, resolved_task))
157                            },
158                        ));
159                    }
160                    lsp_tasks
161                        .entry(source_kind)
162                        .or_insert_with(Vec::new)
163                        .append(&mut new_lsp_tasks);
164                }
165            }
166            lsp_tasks.into_iter().collect()
167        })
168        .race({
169            // `lsp::LSP_REQUEST_TIMEOUT` is larger than we want for the modal to open fast
170            let timer = cx.background_executor().timer(Duration::from_millis(200));
171            async move {
172                timer.await;
173                log::info!("Timed out waiting for LSP tasks");
174                Vec::new()
175            }
176        })
177        .await
178    })
179}