1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use futures::StreamExt;
4use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
5use lsp::LanguageServerBinary;
6use node_runtime::NodeRuntime;
7use smol::fs;
8use std::{
9 any::Any,
10 ffi::OsString,
11 path::{Path, PathBuf},
12 sync::Arc,
13};
14use util::{async_maybe, ResultExt};
15
16const SERVER_PATH: &str = "node_modules/.bin/prisma-language-server";
17
18fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
19 vec![server_path.into(), "--stdio".into()]
20}
21
22pub struct PrismaLspAdapter {
23 node: Arc<dyn NodeRuntime>,
24}
25
26impl PrismaLspAdapter {
27 pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
28 PrismaLspAdapter { node }
29 }
30}
31
32#[async_trait]
33impl LspAdapter for PrismaLspAdapter {
34 fn name(&self) -> LanguageServerName {
35 LanguageServerName("prisma-language-server".into())
36 }
37
38 async fn fetch_latest_server_version(
39 &self,
40 _: &dyn LspAdapterDelegate,
41 ) -> Result<Box<dyn 'static + Any + Send>> {
42 Ok(Box::new(
43 self.node
44 .npm_package_latest_version("@prisma/language-server")
45 .await?,
46 ) as Box<_>)
47 }
48
49 async fn fetch_server_binary(
50 &self,
51 version: Box<dyn 'static + Send + Any>,
52 container_dir: PathBuf,
53 _: &dyn LspAdapterDelegate,
54 ) -> Result<LanguageServerBinary> {
55 let version = version.downcast::<String>().unwrap();
56 let server_path = container_dir.join(SERVER_PATH);
57
58 if fs::metadata(&server_path).await.is_err() {
59 self.node
60 .npm_install_packages(
61 &container_dir,
62 &[("@prisma/language-server", version.as_str())],
63 )
64 .await?;
65 }
66
67 Ok(LanguageServerBinary {
68 path: self.node.binary_path().await?,
69 env: None,
70 arguments: server_binary_arguments(&server_path),
71 })
72 }
73
74 async fn cached_server_binary(
75 &self,
76 container_dir: PathBuf,
77 _: &dyn LspAdapterDelegate,
78 ) -> Option<LanguageServerBinary> {
79 get_cached_server_binary(container_dir, &*self.node).await
80 }
81
82 async fn installation_test_binary(
83 &self,
84 container_dir: PathBuf,
85 ) -> Option<LanguageServerBinary> {
86 get_cached_server_binary(container_dir, &*self.node).await
87 }
88
89 fn initialization_options(&self) -> Option<serde_json::Value> {
90 None
91 }
92}
93
94async fn get_cached_server_binary(
95 container_dir: PathBuf,
96 node: &dyn NodeRuntime,
97) -> Option<LanguageServerBinary> {
98 async_maybe!({
99 let mut last_version_dir = None;
100 let mut entries = fs::read_dir(&container_dir).await?;
101 while let Some(entry) = entries.next().await {
102 let entry = entry?;
103 if entry.file_type().await?.is_dir() {
104 last_version_dir = Some(entry.path());
105 }
106 }
107 let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
108 let server_path = last_version_dir.join(SERVER_PATH);
109 if server_path.exists() {
110 Ok(LanguageServerBinary {
111 path: node.binary_path().await?,
112 env: None,
113 arguments: server_binary_arguments(&server_path),
114 })
115 } else {
116 Err(anyhow!(
117 "missing executable in directory {:?}",
118 last_version_dir
119 ))
120 }
121 })
122 .await
123 .log_err()
124}