Get ESLint to launch and provide diagnostics

Julia and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

crates/lsp/src/lsp.rs                  |  4 +
crates/project/src/project.rs          |  1 
crates/project/src/worktree.rs         | 13 +++-
crates/zed/src/languages.rs            |  6 +-
crates/zed/src/languages/typescript.rs | 72 +++++++++++++++++++++++++--
5 files changed, 82 insertions(+), 14 deletions(-)

Detailed changes

crates/lsp/src/lsp.rs 🔗

@@ -250,6 +250,10 @@ impl LanguageServer {
             log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer));
 
             if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
+                dbg!(
+                    msg.method,
+                    notification_handlers.lock().keys().collect::<Vec<_>>()
+                );
                 if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
                     handler(msg.id, msg.params.get(), cx.clone());
                 } else {

crates/project/src/project.rs 🔗

@@ -2217,6 +2217,7 @@ impl Project {
                         move |params, mut cx| {
                             let languages = languages.clone();
                             async move {
+                                dbg!(&params.items);
                                 let workspace_config =
                                     cx.update(|cx| languages.workspace_configuration(cx)).await;
                                 Ok(params

crates/project/src/worktree.rs 🔗

@@ -536,10 +536,15 @@ impl LocalWorktree {
                 .insert(PathKey(worktree_path.clone()), new_summary);
             let diagnostics_by_server_id =
                 self.diagnostics.entry(worktree_path.clone()).or_default();
-            let ix = match diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
-                Ok(ix) | Err(ix) => ix,
-            };
-            diagnostics_by_server_id[ix] = (server_id, diagnostics);
+            match diagnostics_by_server_id.binary_search_by_key(&server_id, |e| e.0) {
+                Ok(ix) => {
+                    diagnostics_by_server_id[ix] = (server_id, diagnostics);
+                }
+
+                Err(ix) => {
+                    diagnostics_by_server_id.insert(ix, (server_id, diagnostics));
+                }
+            }
         }
 
         let updated = !old_summary.is_empty() || !new_summary.is_empty();

crates/zed/src/languages.rs 🔗

@@ -90,7 +90,7 @@ pub fn init(
             "tsx",
             tree_sitter_typescript::language_tsx(),
             vec![
-                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                // adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
                 adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
             ],
         ),
@@ -98,7 +98,7 @@ pub fn init(
             "typescript",
             tree_sitter_typescript::language_typescript(),
             vec![
-                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                // adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
                 adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
             ],
         ),
@@ -106,7 +106,7 @@ pub fn init(
             "javascript",
             tree_sitter_typescript::language_tsx(),
             vec![
-                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                // adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
                 adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
             ],
         ),

crates/zed/src/languages/typescript.rs 🔗

@@ -1,14 +1,16 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
-use futures::StreamExt;
+use futures::{future::BoxFuture, FutureExt, StreamExt};
+use gpui::{AppContext, Task};
 use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
 use lsp::CodeActionKind;
 use node_runtime::NodeRuntime;
-use serde_json::json;
+use serde_json::{json, Map, Value};
 use smol::fs;
 use std::{
     any::Any,
     ffi::OsString,
+    future,
     path::{Path, PathBuf},
     sync::Arc,
 };
@@ -25,7 +27,7 @@ fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
 }
 
 fn eslint_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
-    vec![server_path.into(), "--stdin".into()]
+    vec![server_path.into(), "--stdio".into()]
 }
 
 pub struct TypeScriptLspAdapter {
@@ -56,7 +58,6 @@ impl LspAdapter for TypeScriptLspAdapter {
         &self,
         _: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Send + Any>> {
-        dbg!();
         Ok(Box::new(TypeScriptVersions {
             typescript_version: self.node.npm_package_latest_version("typescript").await?,
             server_version: self
@@ -173,15 +174,67 @@ pub struct EsLintLspAdapter {
 }
 
 impl EsLintLspAdapter {
-    const SERVER_PATH: &'static str = "node_modules/eslint/bin/eslint.js";
+    const SERVER_PATH: &'static str =
+        "node_modules/vscode-langservers-extracted/lib/eslint-language-server/eslintServer.js";
 
     pub fn new(node: Arc<NodeRuntime>) -> Self {
         EsLintLspAdapter { node }
     }
 }
 
+// "workspaceFolder": {
+//     "name": "testing_ts",
+//     "uri": "file:///Users/julia/Stuff/testing_ts"
+// },
+// "workingDirectory": "file:///Users/julia/Stuff/testing_ts",
+// "nodePath": "/opt/homebrew/opt/node@18/bin/node",
+// "experimental": {},
+
 #[async_trait]
 impl LspAdapter for EsLintLspAdapter {
+    fn workspace_configuration(&self, _: &mut AppContext) -> Option<BoxFuture<'static, Value>> {
+        Some(
+            future::ready(json!({
+                "": {
+                      "validate": "on",
+                      "packageManager": "npm",
+                      "useESLintClass": false,
+                      "experimental": {
+                        "useFlatConfig": false
+                      },
+                      "codeActionOnSave": {
+                        "mode": "all"
+                      },
+                      "format": false,
+                      "quiet": false,
+                      "onIgnoredFiles": "off",
+                      "options": {},
+                      "rulesCustomizations": [],
+                      "run": "onType",
+                      "problems": {
+                        "shortenToSingleLine": false
+                      },
+                      "nodePath": null,
+                      "workspaceFolder": {
+                        "name": "testing_ts",
+                        "uri": "file:///Users/julia/Stuff/testing_ts"
+                      },
+                      "codeAction": {
+                        "disableRuleComment": {
+                          "enable": true,
+                          "location": "separateLine",
+                          "commentStyle": "line"
+                        },
+                        "showDocumentation": {
+                          "enable": true
+                        }
+                      }
+                }
+            }))
+            .boxed(),
+        )
+    }
+
     async fn name(&self) -> LanguageServerName {
         LanguageServerName("eslint".into())
     }
@@ -191,7 +244,9 @@ impl LspAdapter for EsLintLspAdapter {
         _: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Send + Any>> {
         Ok(Box::new(
-            self.node.npm_package_latest_version("eslint").await?,
+            self.node
+                .npm_package_latest_version("vscode-langservers-extracted")
+                .await?,
         ))
     }
 
@@ -206,7 +261,10 @@ impl LspAdapter for EsLintLspAdapter {
 
         if fs::metadata(&server_path).await.is_err() {
             self.node
-                .npm_install_packages([("eslint", version.as_str())], &container_dir)
+                .npm_install_packages(
+                    [("vscode-langservers-extracted", version.as_str())],
+                    &container_dir,
+                )
                 .await?;
         }