csharp: Use lsp settings to locate binary if present (#15885)

Jonathan Dickinson and Marshall Bowers created

C# extension now honors the "lsp" config section

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

docs/src/languages/csharp.md    | 17 ++++++++++++
extensions/csharp/src/csharp.rs | 49 +++++++++++++++++++++++++++-------
2 files changed, 56 insertions(+), 10 deletions(-)

Detailed changes

docs/src/languages/csharp.md 🔗

@@ -1,3 +1,20 @@
 # C#
 
 C# support is available through the [C# extension](https://github.com/zed-industries/zed/tree/main/extensions/csharp).
+
+## Configuration
+
+The `OmniSharp` binary can be configured in a Zed settings file with:
+
+```jsonc
+{
+  "lsp": {
+    "omnisharp": {
+      "binary": {
+        "path": "/path/to/OmniSharp",
+        "args": ["optional", "additional", "args", "-lsp"],
+      },
+    },
+  },
+}
+```

extensions/csharp/src/csharp.rs 🔗

@@ -1,23 +1,48 @@
 use std::fs;
-use zed_extension_api::{self as zed, Result};
+use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Result};
+
+struct OmnisharpBinary {
+    path: String,
+    args: Option<Vec<String>>,
+}
 
 struct CsharpExtension {
     cached_binary_path: Option<String>,
 }
 
 impl CsharpExtension {
-    fn language_server_binary_path(
+    fn language_server_binary(
         &mut self,
-        language_server_id: &zed::LanguageServerId,
+        language_server_id: &LanguageServerId,
         worktree: &zed::Worktree,
-    ) -> Result<String> {
+    ) -> Result<OmnisharpBinary> {
+        let binary_settings = LspSettings::for_worktree("omnisharp", worktree)
+            .ok()
+            .and_then(|lsp_settings| lsp_settings.binary);
+        let binary_args = binary_settings
+            .as_ref()
+            .and_then(|binary_settings| binary_settings.arguments.clone());
+
+        if let Some(path) = binary_settings.and_then(|binary_settings| binary_settings.path) {
+            return Ok(OmnisharpBinary {
+                path,
+                args: binary_args,
+            });
+        }
+
         if let Some(path) = worktree.which("OmniSharp") {
-            return Ok(path);
+            return Ok(OmnisharpBinary {
+                path,
+                args: binary_args,
+            });
         }
 
         if let Some(path) = &self.cached_binary_path {
-            if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
-                return Ok(path.clone());
+            if fs::metadata(&path).map_or(false, |stat| stat.is_file()) {
+                return Ok(OmnisharpBinary {
+                    path: path.clone(),
+                    args: binary_args,
+                });
             }
         }
 
@@ -88,7 +113,10 @@ impl CsharpExtension {
         }
 
         self.cached_binary_path = Some(binary_path.clone());
-        Ok(binary_path)
+        Ok(OmnisharpBinary {
+            path: binary_path,
+            args: binary_args,
+        })
     }
 }
 
@@ -104,9 +132,10 @@ impl zed::Extension for CsharpExtension {
         language_server_id: &zed::LanguageServerId,
         worktree: &zed::Worktree,
     ) -> Result<zed::Command> {
+        let omnisharp_binary = self.language_server_binary(language_server_id, worktree)?;
         Ok(zed::Command {
-            command: self.language_server_binary_path(language_server_id, worktree)?,
-            args: vec!["-lsp".to_string()],
+            command: omnisharp_binary.path,
+            args: omnisharp_binary.args.unwrap_or_else(|| vec!["-lsp".into()]),
             env: Default::default(),
         })
     }