1use std::cell::{Cell, RefCell, RefMut};
2use std::ops::{Deref, DerefMut};
3use std::sync::{Arc, Mutex, MutexGuard};
4
5use super::ErrorReport;
6use neovim_lib::{Neovim, NeovimApi};
7
8#[derive(Clone, Copy, PartialEq)]
9enum NeovimClientState {
10 Uninitialized,
11 InitInProgress,
12 Initialized,
13 Error,
14}
15
16pub enum NeovimRef<'a> {
17 SingleThreaded(RefMut<'a, Neovim>),
18 MultiThreaded(MutexGuard<'a, Option<Neovim>>),
19}
20
21impl<'a> NeovimRef<'a> {
22 fn from_nvim(nvim: RefMut<'a, Neovim>) -> Self {
23 NeovimRef::SingleThreaded(nvim)
24 }
25
26 fn try_nvim_async(nvim_async: &'a NeovimClientAsync) -> Option<NeovimRef<'a>> {
27 let guard = nvim_async.nvim.try_lock();
28
29 if let Ok(guard) = guard {
30 if guard.is_some() {
31 return Some(NeovimRef::MultiThreaded(guard));
32 }
33 }
34
35 None
36 }
37
38 fn from_nvim_async(nvim_async: &'a NeovimClientAsync) -> Option<NeovimRef<'a>> {
39 let guard = nvim_async.nvim.lock().unwrap();
40
41 if guard.is_some() {
42 Some(NeovimRef::MultiThreaded(guard))
43 } else {
44 None
45 }
46 }
47
48 pub fn non_blocked(mut self) -> Option<Self> {
49 self.get_mode().ok_and_report().and_then(|mode| {
50 mode.iter()
51 .find(|kv| kv.0.as_str().map(|key| key == "blocking").unwrap_or(false))
52 .map(|kv| kv.1.as_bool().unwrap_or(false))
53 .and_then(|block| if block { None } else { Some(self) })
54 })
55 }
56}
57
58impl<'a> Deref for NeovimRef<'a> {
59 type Target = Neovim;
60
61 fn deref(&self) -> &Neovim {
62 match *self {
63 NeovimRef::SingleThreaded(ref nvim) => &*nvim,
64 NeovimRef::MultiThreaded(ref nvim) => (&*nvim).as_ref().unwrap(),
65 }
66 }
67}
68
69impl<'a> DerefMut for NeovimRef<'a> {
70 fn deref_mut(&mut self) -> &mut Neovim {
71 match *self {
72 NeovimRef::SingleThreaded(ref mut nvim) => &mut *nvim,
73 NeovimRef::MultiThreaded(ref mut nvim) => (&mut *nvim).as_mut().unwrap(),
74 }
75 }
76}
77
78pub struct NeovimClientAsync {
79 nvim: Arc<Mutex<Option<Neovim>>>,
80}
81
82impl NeovimClientAsync {
83 fn new() -> Self {
84 NeovimClientAsync {
85 nvim: Arc::new(Mutex::new(None)),
86 }
87 }
88
89 pub fn borrow(&self) -> Option<NeovimRef> {
90 NeovimRef::from_nvim_async(self)
91 }
92
93 pub fn try_borrow(&self) -> Option<NeovimRef> {
94 NeovimRef::try_nvim_async(self)
95 }
96}
97
98impl Clone for NeovimClientAsync {
99 fn clone(&self) -> Self {
100 NeovimClientAsync {
101 nvim: self.nvim.clone(),
102 }
103 }
104}
105
106pub struct NeovimClient {
107 state: Cell<NeovimClientState>,
108 nvim: RefCell<Option<Neovim>>,
109 nvim_async: NeovimClientAsync,
110}
111
112impl NeovimClient {
113 pub fn new() -> Self {
114 NeovimClient {
115 state: Cell::new(NeovimClientState::Uninitialized),
116 nvim: RefCell::new(None),
117 nvim_async: NeovimClientAsync::new(),
118 }
119 }
120
121 pub fn clear(&self) {
122 let mut nvim = self.nvim.borrow_mut();
123 if nvim.is_some() {
124 nvim.take();
125 } else {
126 self.nvim_async.nvim.lock().unwrap().take();
127 }
128 }
129
130 pub fn async_to_sync(&self) {
131 let mut lock = self.nvim_async.nvim.lock().unwrap();
132 let nvim = lock.take().unwrap();
133 *self.nvim.borrow_mut() = Some(nvim);
134 }
135
136 pub fn set_nvim_async(&self, nvim: Neovim) -> NeovimClientAsync {
137 *self.nvim_async.nvim.lock().unwrap() = Some(nvim);
138 self.nvim_async.clone()
139 }
140
141 pub fn set_initialized(&self) {
142 self.state.set(NeovimClientState::Initialized);
143 }
144
145 pub fn set_error(&self) {
146 self.state.set(NeovimClientState::Error);
147 }
148
149 pub fn set_in_progress(&self) {
150 self.state.set(NeovimClientState::InitInProgress);
151 }
152
153 pub fn is_initialized(&self) -> bool {
154 self.state.get() == NeovimClientState::Initialized
155 }
156
157 pub fn is_uninitialized(&self) -> bool {
158 self.state.get() == NeovimClientState::Uninitialized
159 }
160
161 pub fn is_initializing(&self) -> bool {
162 self.state.get() == NeovimClientState::InitInProgress
163 }
164
165 pub fn try_nvim(&self) -> Option<NeovimRef> {
168 let nvim = self.nvim.borrow_mut();
169 if nvim.is_some() {
170 Some(NeovimRef::from_nvim(RefMut::map(nvim, |n| {
171 n.as_mut().unwrap()
172 })))
173 } else {
174 self.nvim_async.try_borrow()
175 }
176 }
177
178 pub fn nvim(&self) -> Option<NeovimRef> {
179 let nvim = self.nvim.borrow_mut();
180 if nvim.is_some() {
181 Some(NeovimRef::from_nvim(RefMut::map(nvim, |n| {
182 n.as_mut().unwrap()
183 })))
184 } else {
185 self.nvim_async.borrow()
186 }
187 }
188}