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().text_anchor.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.read_with(cx, |project, _| {
 64        (project.worktree_store(), project.environment().clone())
 65    });
 66
 67    let worktree_abs_path = cx.update(|cx| {
 68        let worktree_id = buffer.read(cx).file().map(|f| f.worktree_id(cx));
 69
 70        worktree_id
 71            .and_then(|worktree_id| worktree_store.read(cx).worktree_for_id(worktree_id, cx))
 72            .and_then(|worktree| worktree.read(cx).root_dir())
 73    });
 74
 75    let project_env = environment
 76        .update(cx, |environment, cx| {
 77            environment.buffer_environment(buffer, &worktree_store, cx)
 78        })
 79        .await;
 80
 81    Some(TaskContext {
 82        cwd: worktree_abs_path.map(|p| p.to_path_buf()),
 83        project_env: project_env.unwrap_or_default(),
 84        ..TaskContext::default()
 85    })
 86}
 87
 88pub fn lsp_tasks(
 89    project: Entity<Project>,
 90    task_sources: &HashMap<LanguageServerName, Vec<BufferId>>,
 91    for_position: Option<text::Anchor>,
 92    cx: &mut App,
 93) -> Task<Vec<(TaskSourceKind, Vec<(Option<LocationLink>, ResolvedTask)>)>> {
 94    let lsp_task_sources = task_sources
 95        .iter()
 96        .filter_map(|(name, buffer_ids)| {
 97            let buffers = buffer_ids
 98                .iter()
 99                .filter(|&&buffer_id| match for_position {
100                    Some(for_position) => for_position.buffer_id == Some(buffer_id),
101                    None => true,
102                })
103                .filter_map(|&buffer_id| project.read(cx).buffer_for_id(buffer_id, cx))
104                .collect::<Vec<_>>();
105
106            let server_id = buffers.iter().find_map(|buffer| {
107                project.read_with(cx, |project, cx| {
108                    project.language_server_id_for_name(buffer.read(cx), name, cx)
109                })
110            });
111            server_id.zip(Some(buffers))
112        })
113        .collect::<Vec<_>>();
114
115    cx.spawn(async move |cx| {
116        cx.spawn(async move |cx| {
117            let mut lsp_tasks = HashMap::default();
118            for (server_id, buffers) in lsp_task_sources {
119                let mut new_lsp_tasks = Vec::new();
120                for buffer in buffers {
121                    let source_kind = match buffer.update(cx, |buffer, _| {
122                        buffer.language().map(|language| language.name())
123                    }) {
124                        Some(language_name) => TaskSourceKind::Lsp {
125                            server: server_id,
126                            language_name: SharedString::from(language_name),
127                        },
128                        None => continue,
129                    };
130                    let id_base = source_kind.to_id_base();
131                    let lsp_buffer_context = lsp_task_context(&project, &buffer, cx)
132                        .await
133                        .unwrap_or_default();
134
135                    let runnables_task = project.update(cx, |project, cx| {
136                        let buffer_id = buffer.read(cx).remote_id();
137                        project.request_lsp(
138                            buffer,
139                            LanguageServerToQuery::Other(server_id),
140                            GetLspRunnables {
141                                buffer_id,
142                                position: for_position,
143                            },
144                            cx,
145                        )
146                    });
147                    if let Some(new_runnables) = runnables_task.await.log_err() {
148                        new_lsp_tasks.extend(new_runnables.runnables.into_iter().filter_map(
149                            |(location, runnable)| {
150                                let resolved_task =
151                                    runnable.resolve_task(&id_base, &lsp_buffer_context)?;
152                                Some((location, resolved_task))
153                            },
154                        ));
155                    }
156                    lsp_tasks
157                        .entry(source_kind)
158                        .or_insert_with(Vec::new)
159                        .append(&mut new_lsp_tasks);
160                }
161            }
162            lsp_tasks.into_iter().collect()
163        })
164        .race({
165            // `lsp::LSP_REQUEST_TIMEOUT` is larger than we want for the modal to open fast
166            let timer = cx.background_executor().timer(Duration::from_millis(200));
167            async move {
168                timer.await;
169                log::info!("Timed out waiting for LSP tasks");
170                Vec::new()
171            }
172        })
173        .await
174    })
175}