use zed::lsp::CompletionKind; use zed::settings::LspSettings; use zed::{CodeLabel, CodeLabelSpan}; use zed_extension_api::{self as zed, serde_json, Result}; struct DartExtension; impl zed::Extension for DartExtension { fn new() -> Self { Self } fn language_server_command( &mut self, _language_server_id: &zed::LanguageServerId, worktree: &zed::Worktree, ) -> Result { let path = worktree .which("dart") .ok_or_else(|| "dart must be installed from dart.dev/get-dart".to_string())?; Ok(zed::Command { command: path, args: vec!["language-server".to_string(), "--protocol=lsp".to_string()], env: Default::default(), }) } fn language_server_workspace_configuration( &mut self, _language_server_id: &zed::LanguageServerId, worktree: &zed::Worktree, ) -> Result> { let settings = LspSettings::for_worktree("dart", worktree) .ok() .and_then(|lsp_settings| lsp_settings.settings.clone()) .unwrap_or_default(); Ok(Some(serde_json::json!({ "dart": settings }))) } fn label_for_completion( &self, _language_server_id: &zed::LanguageServerId, completion: zed::lsp::Completion, ) -> Option { let arrow = " → "; match completion.kind? { CompletionKind::Class => Some(CodeLabel { filter_range: (0..completion.label.len()).into(), spans: vec![CodeLabelSpan::literal( completion.label, Some("type".into()), )], code: String::new(), }), CompletionKind::Function | CompletionKind::Constructor | CompletionKind::Method => { let mut parts = completion.detail.as_ref()?.split(arrow); let (name, _) = completion.label.split_once('(')?; let parameter_list = parts.next()?; let return_type = parts.next()?; let fn_name = " a"; let fat_arrow = " => "; let call_expr = "();"; let code = format!("{return_type}{fn_name}{parameter_list}{fat_arrow}{name}{call_expr}"); let parameter_list_start = return_type.len() + fn_name.len(); Some(CodeLabel { spans: vec![ CodeLabelSpan::code_range( code.len() - call_expr.len() - name.len()..code.len() - call_expr.len(), ), CodeLabelSpan::code_range( parameter_list_start..parameter_list_start + parameter_list.len(), ), CodeLabelSpan::literal(arrow, None), CodeLabelSpan::code_range(0..return_type.len()), ], filter_range: (0..name.len()).into(), code, }) } CompletionKind::Property => { let class_start = "class A {"; let get = " get "; let property_end = " => a; }"; let ty = completion.detail?; let name = completion.label; let code = format!("{class_start}{ty}{get}{name}{property_end}"); let name_start = class_start.len() + ty.len() + get.len(); Some(CodeLabel { spans: vec![ CodeLabelSpan::code_range(name_start..name_start + name.len()), CodeLabelSpan::literal(arrow, None), CodeLabelSpan::code_range(class_start.len()..class_start.len() + ty.len()), ], filter_range: (0..name.len()).into(), code, }) } CompletionKind::Variable => { let name = completion.label; Some(CodeLabel { filter_range: (0..name.len()).into(), spans: vec![CodeLabelSpan::literal(name, Some("variable".into()))], code: String::new(), }) } _ => None, } } } zed::register_extension!(DartExtension);