lsp_ext.rs

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