tailwind: Allow configuring custom tailwind server build (#11921)

Thorsten Ball created

This adds the ability to configure the `tailwindcss-language-server`
integration to use a custom build of the server.

Example configuration in Zed `settings.json`:

```json
{
  "lsp": {
    "tailwindcss-language-server": {
      "binary": {
        "arguments": [
          "/Users/username/tailwindcss-intellisense/packages/tailwindcss-language-server/bin/tailwindcss-language-server",
          "--stdio"
        ]
      }
    }
  }
}
```

This will cause Zed to use its own Node version and run it with the
given arguments.

**Note**: you need to provide `--stdio` as the second argument!

It's also possible to use a custom Node binary:

```json
{
  "lsp": {
    "tailwindcss-language-server": {
      "binary": {
        "path": "/Users/username/bin/my-node",
        "arguments": [
          "/Users/username/tailwindcss-intellisense/packages/tailwindcss-language-server/bin/tailwindcss-language-server",
          "--stdio"
        ]
      }
    }
  }
}
```

This is *super handy* when debugging the language server.

Release Notes:

- Added ability to configure own build of `tailwindcss-language-server`
in Zed settings. Example:
`{"lsp":{"tailwindcss-language-server":{"binary":{"arguments":["/absolute/path/to/tailwindcss-language-server/bin/tailwindcss-language-server",
"--stdio" ]}}}}`

Change summary

crates/languages/src/tailwind.rs | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)

Detailed changes

crates/languages/src/tailwind.rs 🔗

@@ -42,6 +42,40 @@ impl LspAdapter for TailwindLspAdapter {
         LanguageServerName(Self::SERVER_NAME.into())
     }
 
+    async fn check_if_user_installed(
+        &self,
+        _delegate: &dyn LspAdapterDelegate,
+        cx: &AsyncAppContext,
+    ) -> Option<LanguageServerBinary> {
+        let configured_binary = cx
+            .update(|cx| {
+                ProjectSettings::get_global(cx)
+                    .lsp
+                    .get(Self::SERVER_NAME)
+                    .and_then(|s| s.binary.clone())
+            })
+            .ok()??;
+
+        let path = if let Some(configured_path) = configured_binary.path.map(PathBuf::from) {
+            configured_path
+        } else {
+            self.node.binary_path().await.ok()?
+        };
+
+        let arguments = configured_binary
+            .arguments
+            .unwrap_or_default()
+            .iter()
+            .map(|arg| arg.into())
+            .collect();
+
+        Some(LanguageServerBinary {
+            path,
+            arguments,
+            env: None,
+        })
+    }
+
     async fn fetch_latest_server_version(
         &self,
         _: &dyn LspAdapterDelegate,