219 lines · 8.3 KB · Rust
RawDownload
use std::ops::Range;use zed::lsp::{Completion, CompletionKind, Symbol, SymbolKind};use zed::{CodeLabel, CodeLabelSpan};use zed_extension_api::{self as zed, Result}; const OPERATOR_CHAR: [char; 17] = [    '~', '!', '?', '%', '<', ':', '.', '$', '&', '*', '+', '-', '/', '=', '>', '@', '^',]; struct OcamlExtension; impl zed::Extension for OcamlExtension {    fn new() -> Self {        Self    }     fn language_server_command(        &mut self,        _language_server_id: &zed::LanguageServerId,        worktree: &zed::Worktree,    ) -> Result<zed::Command> {        let path = worktree.which("ocamllsp").ok_or_else(|| {            "ocamllsp (ocaml-language-server) must be installed manually.".to_string()        })?;         Ok(zed::Command {            command: path,            args: Vec::new(),            env: Default::default(),        })    }     fn label_for_completion(        &self,        _language_server_id: &zed::LanguageServerId,        completion: Completion,    ) -> Option<CodeLabel> {        let name = &completion.label;        let detail = completion.detail.as_ref().map(|s| s.replace('\n', " "));         match completion.kind.zip(detail) {            Some((CompletionKind::Constructor | CompletionKind::EnumMember, detail)) => {                let (argument, return_t) = detail                    .split_once("->")                    .map_or((None, detail.as_str()), |(arg, typ)| {                        (Some(arg.trim()), typ.trim())                    });                 let type_decl = "type t = ";                let type_of = argument.map(|_| " of ").unwrap_or_default();                let argument = argument.unwrap_or_default();                let terminator = "\n";                let let_decl = "let _ ";                let let_colon = ": ";                let let_suffix = " = ()";                let code = format!(                    "{type_decl}{name}{type_of}{argument}{terminator}{let_decl}{let_colon}{return_t}{let_suffix}"                );                 let name_start = type_decl.len();                let argument_end = name_start + name.len() + type_of.len() + argument.len();                let colon_start = argument_end + terminator.len() + let_decl.len();                let return_type_end = code.len() - let_suffix.len();                Some(CodeLabel {                    code,                    spans: vec![                        CodeLabelSpan::code_range(name_start..argument_end),                        CodeLabelSpan::code_range(colon_start..return_type_end),                    ],                    filter_range: (0..name.len()).into(),                })            }             Some((CompletionKind::Field, detail)) => {                let filter_range_start = if name.starts_with(&['~', '?']) { 1 } else { 0 };                 let record_prefix = "type t = { ";                let record_suffix = "; }";                let code = format!("{record_prefix}{name} : {detail}{record_suffix}");                 Some(CodeLabel {                    spans: vec![CodeLabelSpan::code_range(                        record_prefix.len()..code.len() - record_suffix.len(),                    )],                    code,                    filter_range: (filter_range_start..name.len()).into(),                })            }             Some((CompletionKind::Value, detail)) => {                let let_prefix = "let ";                let suffix = " = ()";                let (l_paren, r_paren) = if name.contains(OPERATOR_CHAR) {                    ("( ", " )")                } else {                    ("", "")                };                let code = format!("{let_prefix}{l_paren}{name}{r_paren} : {detail}{suffix}");                 let name_start = let_prefix.len() + l_paren.len();                let name_end = name_start + name.len();                let type_annotation_start = name_end + r_paren.len();                let type_annotation_end = code.len() - suffix.len();                 Some(CodeLabel {                    spans: vec![                        CodeLabelSpan::code_range(name_start..name_end),                        CodeLabelSpan::code_range(type_annotation_start..type_annotation_end),                    ],                    filter_range: (0..name.len()).into(),                    code,                })            }             Some((CompletionKind::Method, detail)) => {                let method_decl = "class c : object method ";                let end = " end";                let code = format!("{method_decl}{name} : {detail}{end}");                 Some(CodeLabel {                    spans: vec![CodeLabelSpan::code_range(                        method_decl.len()..code.len() - end.len(),                    )],                    code,                    filter_range: (0..name.len()).into(),                })            }             Some((kind, _)) => {                let highlight_name = match kind {                    CompletionKind::Module | CompletionKind::Interface => "title",                    CompletionKind::Keyword => "keyword",                    CompletionKind::TypeParameter => "type",                    _ => return None,                };                 Some(CodeLabel {                    spans: vec![(CodeLabelSpan::literal(name, Some(highlight_name.to_string())))],                    filter_range: (0..name.len()).into(),                    code: String::new(),                })            }            _ => None,        }    }     fn label_for_symbol(        &self,        _language_server_id: &zed::LanguageServerId,        symbol: Symbol,    ) -> Option<CodeLabel> {        let name = &symbol.name;         let (code, filter_range, display_range) = match symbol.kind {            SymbolKind::Property => {                let code = format!("type t = {{ {}: (); }}", name);                let filter_range: Range<usize> = 0..name.len();                let display_range = 11..11 + name.len();                (code, filter_range, display_range)            }            SymbolKind::Function                if name.contains(OPERATOR_CHAR)                    || (name.starts_with("let") && name.contains(OPERATOR_CHAR)) =>            {                let code = format!("let ( {name} ) () = ()");                 let filter_range = 6..6 + name.len();                let display_range = 0..filter_range.end + 1;                (code, filter_range, display_range)            }            SymbolKind::Function => {                let code = format!("let {name} () = ()");                 let filter_range = 4..4 + name.len();                let display_range = 0..filter_range.end;                (code, filter_range, display_range)            }            SymbolKind::Constructor => {                let code = format!("type t = {name}");                let filter_range = 0..name.len();                let display_range = 9..9 + name.len();                (code, filter_range, display_range)            }            SymbolKind::Module => {                let code = format!("module {name} = struct end");                let filter_range = 7..7 + name.len();                let display_range = 0..filter_range.end;                (code, filter_range, display_range)            }            SymbolKind::Class => {                let code = format!("class {name} = object end");                let filter_range = 6..6 + name.len();                let display_range = 0..filter_range.end;                (code, filter_range, display_range)            }            SymbolKind::Method => {                let code = format!("class c = object method {name} = () end");                let filter_range = 0..name.len();                let display_range = 17..24 + name.len();                (code, filter_range, display_range)            }            SymbolKind::String => {                let code = format!("type {name} = T");                let filter_range = 5..5 + name.len();                let display_range = 0..filter_range.end;                (code, filter_range, display_range)            }            _ => return None,        };         Some(CodeLabel {            code,            spans: vec![CodeLabelSpan::code_range(display_range)],            filter_range: filter_range.into(),        })    }} zed::register_extension!(OcamlExtension);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t