neovim_lib/
session.rs

1use std::io::Result;
2use std::io::{Error, ErrorKind, Stdin, Stdout};
3use std::net::TcpStream;
4use std::process::Stdio;
5use std::process::{Child, ChildStdin, ChildStdout, Command};
6use std::result;
7use std::sync::mpsc;
8use std::thread::JoinHandle;
9use std::time::Duration;
10
11use std::path::Path;
12#[cfg(unix)]
13use unix_socket::UnixStream;
14
15use rpc;
16use rpc::handler::{DefaultHandler, Handler, RequestHandler};
17use rpc::Client;
18
19use async::AsyncCall;
20
21use rmpv::Value;
22
23/// An active Neovim session.
24pub struct Session {
25    client: ClientConnection,
26    timeout: Option<Duration>,
27}
28
29macro_rules! call_args {
30    () => (Vec::new());
31    ($($e:expr), +,) => (call_args![$($e),*]);
32    ($($e:expr), +) => {{
33        let mut vec = Vec::new();
34        $(
35            vec.push($e.into_val());
36        )*
37        vec
38    }};
39}
40
41impl Session {
42    /// Connect to nvim instance via tcp
43    pub fn new_tcp(addr: &str) -> Result<Session> {
44        let stream = TcpStream::connect(addr)?;
45        let read = stream.try_clone()?;
46        Ok(Session {
47            client: ClientConnection::Tcp(Client::new(stream, read)),
48            timeout: Some(Duration::new(5, 0)),
49        })
50    }
51
52    #[cfg(unix)]
53    /// Connect to nvim instance via unix socket
54    pub fn new_unix_socket<P: AsRef<Path>>(path: P) -> Result<Session> {
55        let stream = UnixStream::connect(path)?;
56        let read = stream.try_clone()?;
57        Ok(Session {
58            client: ClientConnection::UnixSocket(Client::new(stream, read)),
59            timeout: Some(Duration::new(5, 0)),
60        })
61    }
62
63    /// Connect to a Neovim instance by spawning a new one.
64    pub fn new_child() -> Result<Session> {
65        if cfg!(target_os = "windows") {
66            Self::new_child_path("nvim.exe")
67        } else {
68            Self::new_child_path("nvim")
69        }
70    }
71
72    /// Connect to a Neovim instance by spawning a new one
73    pub fn new_child_path<S: AsRef<Path>>(program: S) -> Result<Session> {
74        Self::new_child_cmd(Command::new(program.as_ref()).arg("--embed"))
75    }
76
77    /// Connect to a Neovim instance by spawning a new one
78    ///
79    /// stdin/stdout settings will be rewrited to `Stdio::piped()`
80    pub fn new_child_cmd(cmd: &mut Command) -> Result<Session> {
81        let mut child = cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
82        let stdout = child
83            .stdout
84            .take()
85            .ok_or_else(|| Error::new(ErrorKind::Other, "Can't open stdout"))?;
86        let stdin = child
87            .stdin
88            .take()
89            .ok_or_else(|| Error::new(ErrorKind::Other, "Can't open stdin"))?;
90
91        Ok(Session {
92            client: ClientConnection::Child(Client::new(stdout, stdin), child),
93            timeout: Some(Duration::new(5, 0)),
94        })
95    }
96
97    /// Connect to a Neovim instance that spawned this process over stdin/stdout.
98    pub fn new_parent() -> Result<Session> {
99        use std::io;
100
101        Ok(Session {
102            client: ClientConnection::Parent(Client::new(io::stdin(), io::stdout())),
103            timeout: Some(Duration::new(5, 0)),
104        })
105    }
106
107    /// Set call timeout
108    pub fn set_timeout(&mut self, timeout: Duration) {
109        self.timeout = Some(timeout);
110    }
111
112    pub fn set_infinity_timeout(&mut self) {
113        self.timeout = None;
114    }
115
116    /// Start processing rpc response and notifications
117    pub fn start_event_loop_channel_handler<H>(
118        &mut self,
119        request_handler: H,
120    ) -> mpsc::Receiver<(String, Vec<Value>)>
121    where
122        H: RequestHandler + Send + 'static,
123    {
124        match self.client {
125            ClientConnection::Child(ref mut client, _) => {
126                client.start_event_loop_channel_handler(request_handler)
127            }
128            ClientConnection::Parent(ref mut client) => {
129                client.start_event_loop_channel_handler(request_handler)
130            }
131            ClientConnection::Tcp(ref mut client) => {
132                client.start_event_loop_channel_handler(request_handler)
133            }
134
135            #[cfg(unix)]
136            ClientConnection::UnixSocket(ref mut client) => {
137                client.start_event_loop_channel_handler(request_handler)
138            }
139        }
140    }
141
142    /// Start processing rpc response and notifications
143    pub fn start_event_loop_channel(&mut self) -> mpsc::Receiver<(String, Vec<Value>)> {
144        self.start_event_loop_channel_handler(DefaultHandler())
145    }
146
147    /// Start processing rpc response and notifications
148    pub fn start_event_loop_handler<H>(&mut self, handler: H)
149    where
150        H: Handler + Send + 'static,
151    {
152        match self.client {
153            ClientConnection::Child(ref mut client, _) => client.start_event_loop_handler(handler),
154            ClientConnection::Parent(ref mut client) => client.start_event_loop_handler(handler),
155            ClientConnection::Tcp(ref mut client) => client.start_event_loop_handler(handler),
156
157            #[cfg(unix)]
158            ClientConnection::UnixSocket(ref mut client) => {
159                client.start_event_loop_handler(handler)
160            }
161        }
162    }
163
164    /// Start processing rpc response and notifications
165    pub fn start_event_loop(&mut self) {
166        match self.client {
167            ClientConnection::Child(ref mut client, _) => client.start_event_loop(),
168            ClientConnection::Parent(ref mut client) => client.start_event_loop(),
169            ClientConnection::Tcp(ref mut client) => client.start_event_loop(),
170
171            #[cfg(unix)]
172            ClientConnection::UnixSocket(ref mut client) => client.start_event_loop(),
173        }
174    }
175
176    /// Sync call. Call can be made only after event loop begin processing
177    pub fn call(&mut self, method: &str, args: Vec<Value>) -> result::Result<Value, Value> {
178        match self.client {
179            ClientConnection::Child(ref mut client, _) => client.call(method, args, self.timeout),
180            ClientConnection::Parent(ref mut client) => client.call(method, args, self.timeout),
181            ClientConnection::Tcp(ref mut client) => client.call(method, args, self.timeout),
182
183            #[cfg(unix)]
184            ClientConnection::UnixSocket(ref mut client) => client.call(method, args, self.timeout),
185        }
186    }
187
188    /// Create async call will be executed when only after call() function.
189    pub fn call_async<R: rpc::FromVal<Value>>(
190        &mut self,
191        method: &str,
192        args: Vec<Value>,
193    ) -> AsyncCall<R> {
194        AsyncCall::new(&mut self.client, method.to_owned(), args)
195    }
196
197    /// Wait dispatch thread to finish.
198    ///
199    /// This can happens in case child process connection is lost for some reason.
200    pub fn take_dispatch_guard(&mut self) -> JoinHandle<()> {
201        match self.client {
202            ClientConnection::Child(ref mut client, _) => client.take_dispatch_guard(),
203            ClientConnection::Parent(ref mut client) => client.take_dispatch_guard(),
204            ClientConnection::Tcp(ref mut client) => client.take_dispatch_guard(),
205
206            #[cfg(unix)]
207            ClientConnection::UnixSocket(ref mut client) => client.take_dispatch_guard(),
208        }
209    }
210}
211
212pub enum ClientConnection {
213    Child(Client<ChildStdout, ChildStdin>, Child),
214    Parent(Client<Stdin, Stdout>),
215    Tcp(Client<TcpStream, TcpStream>),
216
217    #[cfg(unix)]
218    UnixSocket(Client<UnixStream, UnixStream>),
219}