lib.rs

  1use plugin::prelude::*;
  2use serde::Deserialize;
  3use serde_json::json;
  4use std::fs;
  5use std::path::PathBuf;
  6
  7#[import]
  8fn command(string: &str) -> Option<Vec<u8>>;
  9
 10const BIN_PATH: &'static str =
 11    "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
 12
 13#[export]
 14pub fn name() -> &'static str {
 15    "vscode-json-languageserver"
 16}
 17
 18#[export]
 19pub fn server_args() -> Vec<String> {
 20    vec!["--stdio".into()]
 21}
 22
 23#[export]
 24pub fn fetch_latest_server_version() -> Option<String> {
 25    #[derive(Deserialize)]
 26    struct NpmInfo {
 27        versions: Vec<String>,
 28    }
 29
 30    let output =
 31        command("npm info vscode-json-languageserver --json").expect("could not run command");
 32    let output = String::from_utf8(output).unwrap();
 33
 34    let mut info: NpmInfo = serde_json::from_str(&output).ok()?;
 35    info.versions.pop()
 36}
 37
 38#[export]
 39pub fn fetch_server_binary(container_dir: PathBuf, version: String) -> Result<PathBuf, String> {
 40    let version_dir = container_dir.join(version.as_str());
 41    fs::create_dir_all(&version_dir)
 42        .map_err(|_| "failed to create version directory".to_string())?;
 43    let binary_path = version_dir.join(BIN_PATH);
 44
 45    if fs::metadata(&binary_path).is_err() {
 46        let output = command(&format!(
 47            "npm install vscode-json-languageserver@{}",
 48            version
 49        ));
 50        let output = output.map(String::from_utf8);
 51        if output.is_none() {
 52            return Err("failed to install vscode-json-languageserver".to_string());
 53        }
 54
 55        if let Some(mut entries) = fs::read_dir(&container_dir).ok() {
 56            while let Some(entry) = entries.next() {
 57                if let Some(entry) = entry.ok() {
 58                    let entry_path = entry.path();
 59                    if entry_path.as_path() != version_dir {
 60                        fs::remove_dir_all(&entry_path).ok();
 61                    }
 62                }
 63            }
 64        }
 65    }
 66
 67    Ok(binary_path)
 68}
 69
 70#[export]
 71pub fn cached_server_binary(container_dir: PathBuf) -> Option<PathBuf> {
 72    let mut last_version_dir = None;
 73    let mut entries = fs::read_dir(&container_dir).ok()?;
 74
 75    while let Some(entry) = entries.next() {
 76        let entry = entry.ok()?;
 77        if entry.file_type().ok()?.is_dir() {
 78            last_version_dir = Some(entry.path());
 79        }
 80    }
 81
 82    let last_version_dir = last_version_dir?;
 83    let bin_path = last_version_dir.join(BIN_PATH);
 84    if bin_path.exists() {
 85        Some(bin_path)
 86    } else {
 87        println!("no binary found");
 88        None
 89    }
 90}
 91
 92// #[export]
 93// pub fn label_for_completion(
 94//     item: &lsp::CompletionItem,
 95//     // language: &language::Language,
 96// ) -> Option<language::CodeLabel> {
 97//     // TODO: Push more of this method down into the plugin.
 98//     use lsp::CompletionItemKind as Kind;
 99//     let len = item.label.len();
100//     let grammar = language.grammar()?;
101//     let kind = format!("{:?}", item.kind?);
102
103//     // TODO: implementation
104
105//     let highlight_id = grammar.highlight_id_for_name(&name)?;
106//     Some(language::CodeLabel {
107//         text: item.label.clone(),
108//         runs: vec![(0..len, highlight_id)],
109//         filter_range: 0..len,
110//     })
111// }
112
113#[export]
114pub fn initialization_options() -> Option<String> {
115    Some("{ \"provideFormatter\": true }".to_string())
116}
117
118#[export]
119pub fn id_for_language(name: String) -> Option<String> {
120    if name == "JSON" {
121        Some("jsonc".into())
122    } else {
123        None
124    }
125}