Detailed changes
@@ -648,7 +648,7 @@
"tab_size": 2
},
"Ruby": {
- "language_servers": ["solargraph", "..."]
+ "language_servers": ["solargraph", "!ruby-lsp", "..."]
}
},
// Zed's Prettier integration settings.
@@ -10,6 +10,10 @@ repository = "https://github.com/zed-industries/zed"
name = "Solargraph"
language = "Ruby"
+[language_servers.ruby-lsp]
+name = "Ruby LSP"
+language = "Ruby"
+
[grammars.ruby]
repository = "https://github.com/tree-sitter/tree-sitter-ruby"
commit = "9d86f3761bb30e8dcc81e754b81d3ce91848477e"
@@ -1,3 +1,5 @@
+mod ruby_lsp;
mod solargraph;
+pub use ruby_lsp::*;
pub use solargraph::*;
@@ -0,0 +1,85 @@
+use zed::{
+ lsp::{Completion, CompletionKind, Symbol, SymbolKind},
+ CodeLabel, CodeLabelSpan,
+};
+use zed_extension_api::{self as zed, Result};
+
+pub struct RubyLsp {}
+
+impl RubyLsp {
+ pub const LANGUAGE_SERVER_ID: &'static str = "ruby-lsp";
+
+ pub fn new() -> Self {
+ Self {}
+ }
+
+ pub fn server_script_path(&mut self, worktree: &zed::Worktree) -> Result<String> {
+ let path = worktree.which("ruby-lsp").ok_or_else(|| {
+ "ruby-lsp must be installed manually. Install it with `gem install ruby-lsp`."
+ .to_string()
+ })?;
+
+ Ok(path)
+ }
+
+ pub fn label_for_completion(&self, completion: Completion) -> Option<CodeLabel> {
+ let highlight_name = match completion.kind? {
+ CompletionKind::Class | CompletionKind::Module => "type",
+ CompletionKind::Constant => "constant",
+ CompletionKind::Method => "function.method",
+ CompletionKind::Reference => "function.method",
+ CompletionKind::Keyword => "keyword",
+ _ => return None,
+ };
+
+ let len = completion.label.len();
+ let name_span = CodeLabelSpan::literal(completion.label, Some(highlight_name.to_string()));
+
+ Some(CodeLabel {
+ code: Default::default(),
+ spans: vec![name_span],
+ filter_range: (0..len).into(),
+ })
+ }
+
+ pub fn label_for_symbol(&self, symbol: Symbol) -> Option<CodeLabel> {
+ let name = &symbol.name;
+
+ return match symbol.kind {
+ SymbolKind::Method => {
+ let code = format!("def {name}; end");
+ let filter_range = 0..name.len();
+ let display_range = 4..4 + name.len();
+
+ Some(CodeLabel {
+ code,
+ spans: vec![CodeLabelSpan::code_range(display_range)],
+ filter_range: filter_range.into(),
+ })
+ }
+ SymbolKind::Class | SymbolKind::Module => {
+ let code = format!("class {name}; end");
+ let filter_range = 0..name.len();
+ let display_range = 6..6 + name.len();
+
+ Some(CodeLabel {
+ code,
+ spans: vec![CodeLabelSpan::code_range(display_range)],
+ filter_range: filter_range.into(),
+ })
+ }
+ SymbolKind::Constant => {
+ let code = name.to_uppercase().to_string();
+ let filter_range = 0..name.len();
+ let display_range = 0..name.len();
+
+ Some(CodeLabel {
+ code,
+ spans: vec![CodeLabelSpan::code_range(display_range)],
+ filter_range: filter_range.into(),
+ })
+ }
+ _ => None,
+ };
+ }
+}
@@ -1,18 +1,23 @@
mod language_servers;
use zed::lsp::{Completion, Symbol};
-use zed::{CodeLabel, LanguageServerId};
+use zed::serde_json::json;
+use zed::{serde_json, CodeLabel, LanguageServerId};
use zed_extension_api::{self as zed, Result};
-use crate::language_servers::Solargraph;
+use crate::language_servers::{RubyLsp, Solargraph};
struct RubyExtension {
solargraph: Option<Solargraph>,
+ ruby_lsp: Option<RubyLsp>,
}
impl zed::Extension for RubyExtension {
fn new() -> Self {
- Self { solargraph: None }
+ Self {
+ solargraph: None,
+ ruby_lsp: None,
+ }
}
fn language_server_command(
@@ -30,6 +35,15 @@ impl zed::Extension for RubyExtension {
env: worktree.shell_env(),
})
}
+ RubyLsp::LANGUAGE_SERVER_ID => {
+ let ruby_lsp = self.ruby_lsp.get_or_insert_with(|| RubyLsp::new());
+
+ Ok(zed::Command {
+ command: ruby_lsp.server_script_path(worktree)?,
+ args: vec![],
+ env: worktree.shell_env(),
+ })
+ }
language_server_id => Err(format!("unknown language server: {language_server_id}")),
}
}
@@ -41,6 +55,7 @@ impl zed::Extension for RubyExtension {
) -> Option<CodeLabel> {
match language_server_id.as_ref() {
Solargraph::LANGUAGE_SERVER_ID => self.solargraph.as_ref()?.label_for_symbol(symbol),
+ RubyLsp::LANGUAGE_SERVER_ID => self.ruby_lsp.as_ref()?.label_for_symbol(symbol),
_ => None,
}
}
@@ -54,9 +69,29 @@ impl zed::Extension for RubyExtension {
Solargraph::LANGUAGE_SERVER_ID => {
self.solargraph.as_ref()?.label_for_completion(completion)
}
+ RubyLsp::LANGUAGE_SERVER_ID => self.ruby_lsp.as_ref()?.label_for_completion(completion),
_ => None,
}
}
+
+ fn language_server_initialization_options(
+ &mut self,
+ language_server_id: &LanguageServerId,
+ _worktree: &zed::Worktree,
+ ) -> Result<Option<serde_json::Value>> {
+ match language_server_id.as_ref() {
+ // We disable diagnostics because ruby-lsp uses pull-based diagnostics,
+ // which Zed doesn't support yet.
+ RubyLsp::LANGUAGE_SERVER_ID => Ok(Some(json!({
+ "enabledFeatures": {
+ "diagnostics": false
+ },
+ "experimentalFeaturesEnabled": true
+ }))),
+
+ _ => Ok(None),
+ }
+ }
}
zed::register_extension!(RubyExtension);