lsp_ext.rs

  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}