Detailed changes
@@ -262,24 +262,31 @@ pub trait LspAdapter: 'static + Send + Sync {
container_dir: PathBuf,
) -> Option<LanguageServerBinary>;
- async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
+ async fn process_diagnostics(&self, d: &mut lsp::PublishDiagnosticsParams) {
+ dbg!(d);
+ }
- async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
+ async fn process_completion(&self, d: &mut lsp::CompletionItem) {
+ dbg!(d);
+ }
async fn label_for_completion(
&self,
- _: &lsp::CompletionItem,
+ item: &lsp::CompletionItem,
_: &Arc<Language>,
) -> Option<CodeLabel> {
+ dbg!(item);
None
}
async fn label_for_symbol(
&self,
- _: &str,
- _: lsp::SymbolKind,
+ name: &str,
+ kind: lsp::SymbolKind,
_: &Arc<Language>,
) -> Option<CodeLabel> {
+ dbg!(name);
+ dbg!(kind);
None
}
@@ -321,7 +328,7 @@ pub struct CodeLabel {
pub filter_range: Range<usize>,
}
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, Debug)]
pub struct LanguageConfig {
pub name: Arc<str>,
pub path_suffixes: Vec<String>,
@@ -810,6 +817,7 @@ impl LanguageRegistry {
.spawn(async move {
let id = language.id;
let queries = (language.get_queries)(&language.path);
+ dbg!(&language.path);
let language =
Language::new(language.config, Some(language.grammar))
.with_lsp_adapters(language.lsp_adapters)
@@ -819,9 +827,11 @@ impl LanguageRegistry {
Ok(language) => {
let language = Arc::new(language);
let mut state = this.state.write();
+
state.add(language.clone());
state.mark_language_loaded(id);
if let Some(mut txs) = state.loading_languages.remove(&id) {
+ dbg!(&name);
for tx in txs.drain(..) {
let _ = tx.send(Ok(language.clone()));
}
@@ -829,6 +839,7 @@ impl LanguageRegistry {
}
Err(err) => {
log::error!("failed to load language {name} - {err}");
+ dbg!(&name);
let mut state = this.state.write();
state.mark_language_loaded(id);
if let Some(mut txs) = state.loading_languages.remove(&id) {
@@ -151,16 +151,17 @@ impl LanguageServer {
let stdin = server.stdin.take().unwrap();
let stout = server.stdout.take().unwrap();
let mut server = Self::new_internal(
- server_id,
+ server_id.clone(),
stdin,
stout,
Some(server),
root_path,
code_action_kinds,
cx,
- |notification| {
+ move |notification| {
log::info!(
- "unhandled notification {}:\n{}",
+ "{} unhandled notification {}:\n{}",
+ server_id,
notification.method,
serde_json::to_string_pretty(
¬ification
@@ -2680,6 +2680,7 @@ impl Project {
key: (WorktreeId, LanguageServerName),
cx: &mut AsyncAppContext,
) -> Result<Option<Arc<LanguageServer>>> {
+ dbg!(language.name());
let setup = Self::setup_pending_language_server(
this,
initialization_options,
@@ -2694,7 +2695,6 @@ impl Project {
Some(language_server) => language_server,
None => return Ok(None),
};
-
let this = match this.upgrade(cx) {
Some(this) => this,
None => return Err(anyhow!("failed to upgrade project handle")),
@@ -13,6 +13,7 @@ mod json;
#[cfg(feature = "plugin_runtime")]
mod language_plugin;
mod lua;
+mod php;
mod python;
mod ruby;
mod rust;
@@ -135,9 +136,13 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
language(
"yaml",
tree_sitter_yaml::language(),
- vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime))],
+ vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
+ );
+ language(
+ "php",
+ tree_sitter_php::language(),
+ vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))],
);
- language("php", tree_sitter_php::language(), vec![]);
}
#[cfg(any(test, feature = "test-support"))]
@@ -0,0 +1,203 @@
+use anyhow::{anyhow, Result};
+use async_compression::futures::bufread::GzipDecoder;
+use async_tar::Archive;
+use async_trait::async_trait;
+use futures::{future::BoxFuture, FutureExt};
+use gpui::AppContext;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::{CodeActionKind, LanguageServerBinary};
+use node_runtime::NodeRuntime;
+use serde_json::{json, Value};
+use smol::{fs, io::BufReader, stream::StreamExt};
+use std::{
+ any::Any,
+ ffi::OsString,
+ future,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
+use util::ResultExt;
+
+fn intelephense_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
+ vec![server_path.into(), "--stdio".into()]
+}
+
+pub struct IntelephenseVersion(String);
+
+pub struct IntelephenseLspAdapter {
+ node: Arc<NodeRuntime>,
+}
+
+impl IntelephenseLspAdapter {
+ const SERVER_PATH: &'static str = "node_modules/intelephense/lib/intelephense.js";
+
+ #[allow(unused)]
+ pub fn new(node: Arc<NodeRuntime>) -> Self {
+ Self { node }
+ }
+}
+
+#[async_trait]
+impl LspAdapter for IntelephenseLspAdapter {
+ async fn name(&self) -> LanguageServerName {
+ LanguageServerName("intelephense".into())
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ _delegate: &dyn LspAdapterDelegate,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ // At the time of writing the latest vscode-eslint release was released in 2020 and requires
+ // special custom LSP protocol extensions be handled to fully initialize. Download the latest
+ // prerelease instead to sidestep this issue
+ dbg!("Strarting fetching server binary version");
+ Ok(Box::new(IntelephenseVersion(
+ self.node.npm_package_latest_version("intelephense").await?,
+ )) as Box<_>)
+ }
+
+ async fn fetch_server_binary(
+ &self,
+ version: Box<dyn 'static + Send + Any>,
+ container_dir: PathBuf,
+ _delegate: &dyn LspAdapterDelegate,
+ ) -> Result<LanguageServerBinary> {
+ dbg!("Strarting fetching server binary");
+ let version = version.downcast::<IntelephenseVersion>().unwrap();
+ let server_path = container_dir.join(Self::SERVER_PATH);
+
+ if fs::metadata(&server_path).await.is_err() {
+ self.node
+ .npm_install_packages(&container_dir, [("intelephense", version.0.as_str())])
+ .await?;
+ }
+ dbg!("Fetched server binary");
+ Ok(LanguageServerBinary {
+ path: dbg!(self.node.binary_path().await)?,
+ arguments: intelephense_server_binary_arguments(&server_path),
+ })
+ }
+
+ async fn cached_server_binary(
+ &self,
+ container_dir: PathBuf,
+ _: &dyn LspAdapterDelegate,
+ ) -> Option<LanguageServerBinary> {
+ dbg!("cached_server_binary");
+ get_cached_server_binary(container_dir, &self.node).await
+ }
+
+ async fn installation_test_binary(
+ &self,
+ container_dir: PathBuf,
+ ) -> Option<LanguageServerBinary> {
+ dbg!("installation_test_binary");
+ get_cached_server_binary(container_dir, &self.node).await
+ }
+
+ async fn label_for_completion(
+ &self,
+ _item: &lsp::CompletionItem,
+ _language: &Arc<language::Language>,
+ ) -> Option<language::CodeLabel> {
+ dbg!(_item.kind);
+ None
+ }
+
+ async fn initialization_options(&self) -> Option<serde_json::Value> {
+ dbg!("init_options");
+ None
+ }
+}
+
+async fn get_cached_server_binary(
+ container_dir: PathBuf,
+ node: &NodeRuntime,
+) -> Option<LanguageServerBinary> {
+ (|| async move {
+ let mut last_version_dir = None;
+ let mut entries = fs::read_dir(&container_dir).await?;
+ while let Some(entry) = entries.next().await {
+ let entry = entry?;
+ if entry.file_type().await?.is_dir() {
+ last_version_dir = Some(entry.path());
+ }
+ }
+ let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
+ let server_path = last_version_dir.join(IntelephenseLspAdapter::SERVER_PATH);
+ if server_path.exists() {
+ Ok(LanguageServerBinary {
+ path: node.binary_path().await?,
+ arguments: intelephense_server_binary_arguments(&server_path),
+ })
+ } else {
+ Err(anyhow!(
+ "missing executable in directory {:?}",
+ last_version_dir
+ ))
+ }
+ })()
+ .await
+ .log_err()
+}
+
+#[cfg(test)]
+mod tests {
+ use gpui::TestAppContext;
+ use unindent::Unindent;
+
+ #[gpui::test]
+ async fn test_outline(cx: &mut TestAppContext) {
+ let language = crate::languages::language("php", tree_sitter_php::language(), None).await;
+
+ /*let text = r#"
+ function a() {
+ // local variables are omitted
+ let a1 = 1;
+ // all functions are included
+ async function a2() {}
+ }
+ // top-level variables are included
+ let b: C
+ function getB() {}
+ // exported variables are included
+ export const d = e;
+ "#
+ .unindent();*/
+ let text = r#"
+ function a() {
+ // local variables are omitted
+ $a1 = 1;
+ // all functions are included
+ function a2() {}
+ }
+ class Foo {}
+ "#
+ .unindent();
+ let buffer =
+ cx.add_model(|cx| language::Buffer::new(0, text, cx).with_language(language, cx));
+ let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
+ panic!(
+ "{:?}",
+ outline
+ .items
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>()
+ );
+ assert_eq!(
+ outline
+ .items
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[
+ ("function a()", 0),
+ ("async function a2()", 1),
+ ("let b", 0),
+ ("function getB()", 0),
+ ("const d", 0),
+ ]
+ );
+ }
+}