Detailed changes
@@ -5720,7 +5720,6 @@ dependencies = [
"tree-sitter-proto",
"tree-sitter-python",
"tree-sitter-regex",
- "tree-sitter-ruby",
"tree-sitter-rust",
"tree-sitter-typescript",
"tree-sitter-yaml",
@@ -13114,6 +13113,13 @@ dependencies = [
"zed_extension_api 0.0.4",
]
+[[package]]
+name = "zed_ruby"
+version = "0.0.1"
+dependencies = [
+ "zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "zed_svelte"
version = "0.0.1"
@@ -127,6 +127,7 @@ members = [
"extensions/php",
"extensions/prisma",
"extensions/purescript",
+ "extensions/ruby",
"extensions/svelte",
"extensions/terraform",
"extensions/toml",
@@ -633,6 +633,9 @@
},
"Prisma": {
"tab_size": 2
+ },
+ "Ruby": {
+ "language_servers": ["solargraph", "..."]
}
},
// Zed's Prettier integration settings.
@@ -58,6 +58,7 @@ const SUGGESTIONS_BY_EXTENSION_ID: &[(&str, &[&str])] = &[
("r", &["r", "R"]),
("racket", &["rkt"]),
("rescript", &["res", "resi"]),
+ ("ruby", &["rb", "erb"]),
("scheme", &["scm"]),
("scss", &["scss"]),
("sql", &["sql"]),
@@ -46,7 +46,6 @@ tree-sitter-markdown.workspace = true
tree-sitter-proto.workspace = true
tree-sitter-python.workspace = true
tree-sitter-regex.workspace = true
-tree-sitter-ruby.workspace = true
tree-sitter-rust.workspace = true
tree-sitter-typescript.workspace = true
tree-sitter-yaml.workspace = true
@@ -16,7 +16,6 @@ mod css;
mod go;
mod json;
mod python;
-mod ruby;
mod rust;
mod tailwind;
mod typescript;
@@ -50,7 +49,6 @@ pub fn init(
("proto", tree_sitter_proto::language()),
("python", tree_sitter_python::language()),
("regex", tree_sitter_regex::language()),
- ("ruby", tree_sitter_ruby::language()),
("rust", tree_sitter_rust::language()),
("tsx", tree_sitter_typescript::language_tsx()),
("typescript", tree_sitter_typescript::language_typescript()),
@@ -156,8 +154,6 @@ pub fn init(
node_runtime.clone(),
))]
);
- language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
- language!("erb", vec![Arc::new(ruby::RubyLanguageServer),]);
language!("regex");
language!(
"yaml",
@@ -1,205 +0,0 @@
-use anyhow::{anyhow, Result};
-use async_trait::async_trait;
-use gpui::AsyncAppContext;
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp::LanguageServerBinary;
-use project::project_settings::{BinarySettings, ProjectSettings};
-use settings::Settings;
-use std::{any::Any, ffi::OsString, path::PathBuf, sync::Arc};
-
-pub struct RubyLanguageServer;
-
-impl RubyLanguageServer {
- const SERVER_NAME: &'static str = "solargraph";
-
- fn server_binary_arguments() -> Vec<OsString> {
- vec!["stdio".into()]
- }
-}
-
-#[async_trait(?Send)]
-impl LspAdapter for RubyLanguageServer {
- fn name(&self) -> LanguageServerName {
- 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())
- });
-
- if let Ok(Some(BinarySettings {
- path: Some(path),
- arguments,
- })) = configured_binary
- {
- Some(LanguageServerBinary {
- path: path.into(),
- arguments: arguments
- .unwrap_or_default()
- .iter()
- .map(|arg| arg.into())
- .collect(),
- env: None,
- })
- } else {
- let env = delegate.shell_env().await;
- let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
- Some(LanguageServerBinary {
- path,
- arguments: Self::server_binary_arguments(),
- env: Some(env),
- })
- }
- }
-
- async fn fetch_latest_server_version(
- &self,
- _: &dyn LspAdapterDelegate,
- ) -> Result<Box<dyn 'static + Any + Send>> {
- Ok(Box::new(()))
- }
-
- async fn fetch_server_binary(
- &self,
- _version: Box<dyn 'static + Send + Any>,
- _container_dir: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Result<LanguageServerBinary> {
- Err(anyhow!("solargraph must be installed manually"))
- }
-
- async fn cached_server_binary(
- &self,
- _: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Option<LanguageServerBinary> {
- Some(LanguageServerBinary {
- path: "solargraph".into(),
- env: None,
- arguments: Self::server_binary_arguments(),
- })
- }
-
- fn can_be_reinstalled(&self) -> bool {
- false
- }
-
- async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
- None
- }
-
- async fn label_for_completion(
- &self,
- item: &lsp::CompletionItem,
- language: &Arc<language::Language>,
- ) -> Option<language::CodeLabel> {
- let label = &item.label;
- let grammar = language.grammar()?;
- let highlight_id = match item.kind? {
- lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
- lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
- lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
- grammar.highlight_id_for_name("type")?
- }
- lsp::CompletionItemKind::KEYWORD => {
- if label.starts_with(':') {
- grammar.highlight_id_for_name("string.special.symbol")?
- } else {
- grammar.highlight_id_for_name("keyword")?
- }
- }
- lsp::CompletionItemKind::VARIABLE => {
- if label.starts_with('@') {
- grammar.highlight_id_for_name("property")?
- } else {
- return None;
- }
- }
- _ => return None,
- };
- Some(language::CodeLabel {
- text: label.clone(),
- runs: vec![(0..label.len(), highlight_id)],
- filter_range: 0..label.len(),
- })
- }
-
- async fn label_for_symbol(
- &self,
- label: &str,
- kind: lsp::SymbolKind,
- language: &Arc<language::Language>,
- ) -> Option<language::CodeLabel> {
- let grammar = language.grammar()?;
- match kind {
- lsp::SymbolKind::METHOD => {
- let mut parts = label.split('#');
- let classes = parts.next()?;
- let method = parts.next()?;
- if parts.next().is_some() {
- return None;
- }
-
- let class_id = grammar.highlight_id_for_name("type")?;
- let method_id = grammar.highlight_id_for_name("function.method")?;
-
- let mut ix = 0;
- let mut runs = Vec::new();
- for (i, class) in classes.split("::").enumerate() {
- if i > 0 {
- ix += 2;
- }
- let end_ix = ix + class.len();
- runs.push((ix..end_ix, class_id));
- ix = end_ix;
- }
-
- ix += 1;
- let end_ix = ix + method.len();
- runs.push((ix..end_ix, method_id));
- Some(language::CodeLabel {
- text: label.to_string(),
- runs,
- filter_range: 0..label.len(),
- })
- }
- lsp::SymbolKind::CONSTANT => {
- let constant_id = grammar.highlight_id_for_name("constant")?;
- Some(language::CodeLabel {
- text: label.to_string(),
- runs: vec![(0..label.len(), constant_id)],
- filter_range: 0..label.len(),
- })
- }
- lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
- let class_id = grammar.highlight_id_for_name("type")?;
-
- let mut ix = 0;
- let mut runs = Vec::new();
- for (i, class) in label.split("::").enumerate() {
- if i > 0 {
- ix += "::".len();
- }
- let end_ix = ix + class.len();
- runs.push((ix..end_ix, class_id));
- ix = end_ix;
- }
-
- Some(language::CodeLabel {
- text: label.to_string(),
- runs,
- filter_range: 0..label.len(),
- })
- }
- _ => return None,
- }
- }
-}
@@ -0,0 +1,16 @@
+[package]
+name = "zed_ruby"
+version = "0.0.1"
+edition = "2021"
+publish = false
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/ruby.rs"
+crate-type = ["cdylib"]
+
+[dependencies]
+zed_extension_api = "0.0.6"
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,15 @@
+id = "ruby"
+name = "Ruby"
+description = "Ruby support."
+version = "0.0.1"
+schema_version = 1
+authors = ["Vitaly Slobodin <vitaliy.slobodin@gmail.com>"]
+repository = "https://github.com/zed-industries/zed"
+
+[language_servers.solargraph]
+name = "Solargraph"
+language = "Ruby"
+
+[grammars.ruby]
+repository = "https://github.com/tree-sitter/tree-sitter-ruby"
+commit = "9d86f3761bb30e8dcc81e754b81d3ce91848477e"
@@ -0,0 +1,3 @@
+mod solargraph;
+
+pub use solargraph::*;
@@ -0,0 +1,121 @@
+use zed::lsp::{Completion, CompletionKind, Symbol, SymbolKind};
+use zed::{CodeLabel, CodeLabelSpan};
+use zed_extension_api::{self as zed, Result};
+
+pub struct Solargraph {}
+
+impl Solargraph {
+ pub const LANGUAGE_SERVER_ID: &'static str = "solargraph";
+
+ pub fn new() -> Self {
+ Self {}
+ }
+
+ pub fn server_script_path(&mut self, worktree: &zed::Worktree) -> Result<String> {
+ let path = worktree
+ .which("solargraph")
+ .ok_or_else(|| "solargraph must be installed manually".to_string())?;
+
+ Ok(path)
+ }
+
+ pub fn label_for_completion(&self, completion: Completion) -> Option<CodeLabel> {
+ match completion.kind? {
+ CompletionKind::Method => {
+ let highlight_name = match completion.kind? {
+ CompletionKind::Class | CompletionKind::Module => "type",
+ CompletionKind::Constant => "constant",
+ CompletionKind::Method => "function.method",
+ CompletionKind::Keyword => {
+ if completion.label.starts_with(':') {
+ "string.special.symbol"
+ } else {
+ "keyword"
+ }
+ }
+ CompletionKind::Variable => {
+ if completion.label.starts_with('@') {
+ "property"
+ } else {
+ return None;
+ }
+ }
+ _ => 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: if let Some(detail) = completion.detail {
+ vec![
+ name_span,
+ CodeLabelSpan::literal(" ", None),
+ CodeLabelSpan::literal(detail, None),
+ ]
+ } else {
+ vec![name_span]
+ },
+ filter_range: (0..len).into(),
+ })
+ }
+ _ => None,
+ }
+ }
+
+ pub fn label_for_symbol(&self, symbol: Symbol) -> Option<CodeLabel> {
+ let name = &symbol.name;
+
+ return match symbol.kind {
+ SymbolKind::Method => {
+ let mut parts = name.split('#');
+ let container_name = parts.next()?;
+ let method_name = parts.next()?;
+
+ if parts.next().is_some() {
+ return None;
+ }
+
+ let filter_range = 0..name.len();
+
+ let spans = vec![
+ CodeLabelSpan::literal(container_name, Some("type".to_string())),
+ CodeLabelSpan::literal("#", None),
+ CodeLabelSpan::literal(method_name, Some("function.method".to_string())),
+ ];
+
+ Some(CodeLabel {
+ code: name.to_string(),
+ spans,
+ filter_range: filter_range.into(),
+ })
+ }
+ SymbolKind::Class | SymbolKind::Module => {
+ let class = "class ";
+ let code = format!("{class}{name}");
+ let filter_range = 0..name.len();
+ let display_range = class.len()..class.len() + 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,
+ };
+ }
+}
@@ -0,0 +1,62 @@
+mod language_servers;
+
+use zed::lsp::{Completion, Symbol};
+use zed::{CodeLabel, LanguageServerId};
+use zed_extension_api::{self as zed, Result};
+
+use crate::language_servers::Solargraph;
+
+struct RubyExtension {
+ solargraph: Option<Solargraph>,
+}
+
+impl zed::Extension for RubyExtension {
+ fn new() -> Self {
+ Self { solargraph: None }
+ }
+
+ fn language_server_command(
+ &mut self,
+ language_server_id: &LanguageServerId,
+ worktree: &zed::Worktree,
+ ) -> Result<zed::Command> {
+ match language_server_id.as_ref() {
+ Solargraph::LANGUAGE_SERVER_ID => {
+ let solargraph = self.solargraph.get_or_insert_with(|| Solargraph::new());
+
+ Ok(zed::Command {
+ command: solargraph.server_script_path(worktree)?,
+ args: vec!["stdio".into()],
+ env: worktree.shell_env(),
+ })
+ }
+ language_server_id => Err(format!("unknown language server: {language_server_id}")),
+ }
+ }
+
+ fn label_for_symbol(
+ &self,
+ language_server_id: &LanguageServerId,
+ symbol: Symbol,
+ ) -> Option<CodeLabel> {
+ match language_server_id.as_ref() {
+ Solargraph::LANGUAGE_SERVER_ID => self.solargraph.as_ref()?.label_for_symbol(symbol),
+ _ => None,
+ }
+ }
+
+ fn label_for_completion(
+ &self,
+ language_server_id: &LanguageServerId,
+ completion: Completion,
+ ) -> Option<CodeLabel> {
+ match language_server_id.as_ref() {
+ Solargraph::LANGUAGE_SERVER_ID => {
+ self.solargraph.as_ref()?.label_for_completion(completion)
+ }
+ _ => None,
+ }
+ }
+}
+
+zed::register_extension!(RubyExtension);