neovim_lib/
neovim.rs

1use neovim_api::NeovimApi;
2use rmpv::Value;
3use rpc::*;
4use session::Session;
5use std::error::Error;
6use std::fmt;
7
8pub struct Neovim {
9    pub session: Session,
10}
11
12pub enum UiOption {
13    RGB(bool),
14    ExtPopupmenu(bool),
15    ExtTabline(bool),
16    ExtCmdline(bool),
17    ExtWildmenu(bool),
18    ExtLinegrid(bool),
19    ExtHlstate(bool),
20    ExtTermcolors(bool),
21}
22
23impl UiOption {
24    fn to_value(&self) -> (Value, Value) {
25        let name_value = self.to_name_value();
26        (name_value.0.into(), name_value.1)
27    }
28
29    fn to_name_value(&self) -> (&'static str, Value) {
30        match *self {
31            UiOption::RGB(val) => ("rgb", val.into()),
32            UiOption::ExtPopupmenu(val) => ("ext_popupmenu", val.into()),
33            UiOption::ExtTabline(val) => ("ext_tabline", val.into()),
34            UiOption::ExtCmdline(val) => ("ext_cmdline", val.into()),
35            UiOption::ExtWildmenu(val) => ("ext_wildmenu", val.into()),
36            UiOption::ExtLinegrid(val) => ("ext_linegrid", val.into()),
37            UiOption::ExtHlstate(val) => ("ext_hlstate", val.into()),
38            UiOption::ExtTermcolors(val) => ("ext_termcolors", val.into()),
39        }
40    }
41}
42
43pub struct UiAttachOptions {
44    options: Vec<(&'static str, UiOption)>,
45}
46
47impl UiAttachOptions {
48    pub fn new() -> UiAttachOptions {
49        UiAttachOptions {
50            options: Vec::new(),
51        }
52    }
53
54    fn set_option(&mut self, option: UiOption) {
55        let name = option.to_name_value();
56        let position = self.options.iter().position(|o| o.0 == name.0);
57
58        if let Some(position) = position {
59            self.options[position].1 = option;
60        } else {
61            self.options.push((name.0, option));
62        }
63    }
64
65    pub fn set_rgb(&mut self, rgb: bool) -> &mut Self {
66        self.set_option(UiOption::RGB(rgb));
67        self
68    }
69
70    pub fn set_popupmenu_external(&mut self, popupmenu_external: bool) -> &mut Self {
71        self.set_option(UiOption::ExtPopupmenu(popupmenu_external));
72        self
73    }
74
75    pub fn set_tabline_external(&mut self, tabline_external: bool) -> &mut Self {
76        self.set_option(UiOption::ExtTabline(tabline_external));
77        self
78    }
79
80    pub fn set_cmdline_external(&mut self, cmdline_external: bool) -> &mut Self {
81        self.set_option(UiOption::ExtCmdline(cmdline_external));
82        self
83    }
84
85    pub fn set_wildmenu_external(&mut self, wildmenu_external: bool) -> &mut Self {
86        self.set_option(UiOption::ExtWildmenu(wildmenu_external));
87        self
88    }
89
90    pub fn set_linegrid_external(&mut self, linegrid_external: bool) -> &mut Self {
91        self.set_option(UiOption::ExtLinegrid(linegrid_external));
92        self
93    }
94
95    pub fn set_hlstate_external(&mut self, hlstate_external: bool) -> &mut Self {
96        self.set_option(UiOption::ExtHlstate(hlstate_external));
97        self
98    }
99
100    pub fn set_termcolors_external(&mut self, termcolors_external: bool) -> &mut Self {
101        self.set_option(UiOption::ExtTermcolors(termcolors_external));
102        self
103    }
104
105    fn to_value_map(&self) -> Value {
106        let map = self.options.iter().map(|o| o.1.to_value()).collect();
107        Value::Map(map)
108    }
109}
110
111#[derive(Debug, PartialEq, Eq, Clone)]
112pub enum CallError {
113    GenericError(String),
114    NeovimError(i64, String),
115}
116
117impl fmt::Display for CallError {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        match *self {
120            CallError::GenericError(ref s) => write!(f, "Unknown error type: {}", s),
121            CallError::NeovimError(id, ref s) => write!(f, "{} - {}", id, s),
122        }
123    }
124}
125
126impl Error for CallError {
127    fn description(&self) -> &str {
128        match *self {
129            CallError::GenericError(ref s) => s,
130            CallError::NeovimError(_, ref s) => s,
131        }
132    }
133}
134
135#[doc(hidden)]
136pub fn map_generic_error(err: Value) -> CallError {
137    match err {
138        Value::String(val) => CallError::GenericError(val.as_str().unwrap().to_owned()),
139        Value::Array(arr) => {
140            if arr.len() == 2 {
141                match (&arr[0], &arr[1]) {
142                    (&Value::Integer(ref id), &Value::String(ref val)) => CallError::NeovimError(
143                        id.as_i64().unwrap(),
144                        val.as_str().unwrap().to_owned(),
145                    ),
146                    _ => CallError::GenericError(format!("{:?}", arr)),
147                }
148            } else {
149                CallError::GenericError(format!("{:?}", arr))
150            }
151        }
152        val => CallError::GenericError(format!("{:?}", val)),
153    }
154}
155
156#[doc(hidden)]
157pub fn map_result<T: FromVal<Value>>(val: Value) -> T {
158    T::from_val(val)
159}
160
161impl Neovim {
162    pub fn new(session: Session) -> Neovim {
163        Neovim { session }
164    }
165
166    /// Register as a remote UI.
167    ///
168    /// After this method is called, the client will receive redraw notifications.
169    pub fn ui_attach(
170        &mut self,
171        width: i64,
172        height: i64,
173        opts: &UiAttachOptions,
174    ) -> Result<(), CallError> {
175        self.session
176            .call(
177                "nvim_ui_attach",
178                call_args!(width, height, opts.to_value_map()),
179            ).map_err(map_generic_error)
180            .map(|_| ())
181    }
182
183    /// Send a quit command to Nvim.
184    /// The quit command is 'qa!' which will make Nvim quit without
185    /// saving anything.
186    pub fn quit_no_save(&mut self) -> Result<(), CallError> {
187        self.command("qa!")
188    }
189
190    /// Same as `ui_set_option` but use `UiOption` as argument to check type at compile time
191    pub fn set_option(&mut self, option: UiOption) -> Result<(), CallError> {
192        let name_value = option.to_name_value();
193        self.ui_set_option(name_value.0, name_value.1)
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    #[test]
202    fn test_ui_options() {
203        let value_map = UiAttachOptions::new()
204            .set_rgb(true)
205            .set_rgb(false)
206            .set_popupmenu_external(true)
207            .to_value_map();
208
209        assert_eq!(
210            Value::Map(vec![
211                ("rgb".into(), false.into()),
212                ("ext_popupmenu".into(), true.into()),
213            ]),
214            value_map
215        );
216    }
217}