Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use std::collections::HashMap;

use anyhow::bail;
use helix_config::{options, List, Map, String, Ty, Value};

use crate::lsp;

// TODO: differentiating between Some(null) and None is not really practical
// since the distinction is lost on a roundtrip trough config::Value.
// Porbably better to change our code to treat null the way we currently
// treat None
options! {
    struct LanguageServerConfig {
        /// The name or path of the language server binary to execute. Binaries must be in `$PATH`
        command: Option<String> = None,
        /// A list of arguments to pass to the language server binary
        #[read = deref]
        args: List<String> = List::default(),
        /// Any environment variables that will be used when starting the language server
        enviorment: Map<String> = Map::default(),
        /// LSP initialization options
        #[name = "config"]
        server_config: Option<Box<serde_json::Value>> = None,
        /// LSP initialization options
        #[read = copy]
        timeout: u64 = 20,
        // TODO: merge
        /// LSP formatting options
        #[name = "config.format"]
        #[read = fold(HashMap::new(), fold_format_config, FormatConfig)]
        format: Map<FormattingProperty> = Map::default()
    }
}

type FormatConfig = HashMap<std::string::String, lsp::FormattingProperty>;

fn fold_format_config(config: &Map<FormattingProperty>, mut res: FormatConfig) -> FormatConfig {
    for (k, v) in config.iter() {
        res.entry(k.to_string()).or_insert_with(|| v.0.clone());
    }
    res
}

// damm orphan rules :/
#[derive(Debug, PartialEq, Clone)]
struct FormattingProperty(lsp::FormattingProperty);

impl Ty for FormattingProperty {
    fn from_value(val: Value) -> anyhow::Result<Self> {
        match val {
            Value::Int(_) => Ok(FormattingProperty(lsp::FormattingProperty::Number(
                i32::from_value(val)?,
            ))),
            Value::Bool(val) => Ok(FormattingProperty(lsp::FormattingProperty::Bool(val))),
            Value::String(val) => Ok(FormattingProperty(lsp::FormattingProperty::String(val))),
            _ => bail!("expected a string, boolean or integer"),
        }
    }

    fn to_value(&self) -> Value {
        match self.0 {
            lsp::FormattingProperty::Bool(val) => Value::Bool(val),
            lsp::FormattingProperty::Number(val) => Value::Int(val as _),
            lsp::FormattingProperty::String(ref val) => Value::String(val.clone()),
        }
    }
}