1 // Copyright 2018 The xi-editor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::io;
16 use std::sync::{Arc, Mutex, MutexGuard, Weak};
17 
18 use serde_json::Value;
19 
20 use xi_rpc::{Error as RpcError, Handler, ReadError, RemoteError, RpcCtx};
21 use xi_trace;
22 
23 use crate::plugin_rpc::{PluginCommand, PluginNotification, PluginRequest};
24 use crate::plugins::{Plugin, PluginId};
25 use crate::rpc::*;
26 use crate::tabs::{CoreState, ViewId};
27 
28 /// A reference to the main core state.
29 ///
30 /// # Note
31 ///
32 /// Various items of initial setup are dependent on how the client
33 /// is configured, so we defer instantiating state until we have that
34 /// information.
35 pub enum XiCore {
36     // TODO: profile startup, and determine what things (such as theme loading)
37     // we should be doing before client_init.
38     Waiting,
39     Running(Arc<Mutex<CoreState>>),
40 }
41 
42 /// A weak reference to the main state. This is passed to plugin threads.
43 #[derive(Clone)]
44 pub struct WeakXiCore(Weak<Mutex<CoreState>>);
45 
46 #[allow(dead_code)]
47 impl XiCore {
new() -> Self48     pub fn new() -> Self {
49         XiCore::Waiting
50     }
51 
52     /// Returns `true` if the `client_started` has not been received.
is_waiting(&self) -> bool53     fn is_waiting(&self) -> bool {
54         match *self {
55             XiCore::Waiting => true,
56             _ => false,
57         }
58     }
59 
60     /// Returns a guard to the core state. A convenience around `Mutex::lock`.
61     ///
62     /// # Panics
63     ///
64     /// Panics if core has not yet received the `client_started` message.
inner(&self) -> MutexGuard<CoreState>65     pub fn inner(&self) -> MutexGuard<CoreState> {
66         match self {
67             XiCore::Running(ref inner) => inner.lock().unwrap(),
68             XiCore::Waiting => panic!(
69                 "core does not start until client_started \
70                  RPC is received"
71             ),
72         }
73     }
74 
75     /// Returns a new reference to the core state, if core is running.
weak_self(&self) -> Option<WeakXiCore>76     fn weak_self(&self) -> Option<WeakXiCore> {
77         match self {
78             XiCore::Running(ref inner) => Some(WeakXiCore(Arc::downgrade(inner))),
79             XiCore::Waiting => None,
80         }
81     }
82 }
83 
84 /// Handler for messages originating with the frontend.
85 impl Handler for XiCore {
86     type Notification = CoreNotification;
87     type Request = CoreRequest;
88 
handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification)89     fn handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification) {
90         use self::CoreNotification::*;
91 
92         // We allow tracing to be enabled before event `client_started`
93         if let TracingConfig { enabled } = rpc {
94             match enabled {
95                 true => xi_trace::enable_tracing(),
96                 false => xi_trace::disable_tracing(),
97             }
98             info!("tracing in core = {:?}", enabled);
99             if self.is_waiting() {
100                 return;
101             }
102         }
103 
104         // wait for client_started before setting up inner
105         if let ClientStarted { ref config_dir, ref client_extras_dir } = rpc {
106             assert!(self.is_waiting(), "client_started can only be sent once");
107             let state =
108                 CoreState::new(ctx.get_peer(), config_dir.clone(), client_extras_dir.clone());
109             let state = Arc::new(Mutex::new(state));
110             *self = XiCore::Running(state);
111             let weak_self = self.weak_self().unwrap();
112             self.inner().finish_setup(weak_self);
113         }
114 
115         self.inner().client_notification(rpc);
116     }
117 
handle_request(&mut self, _ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError>118     fn handle_request(&mut self, _ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError> {
119         self.inner().client_request(rpc)
120     }
121 
idle(&mut self, _ctx: &RpcCtx, token: usize)122     fn idle(&mut self, _ctx: &RpcCtx, token: usize) {
123         self.inner().handle_idle(token);
124     }
125 }
126 
127 impl WeakXiCore {
128     /// Attempts to upgrade the weak reference. Essentially a wrapper
129     /// for `Arc::upgrade`.
upgrade(&self) -> Option<XiCore>130     fn upgrade(&self) -> Option<XiCore> {
131         self.0.upgrade().map(XiCore::Running)
132     }
133 
134     /// Called immediately after attempting to start a plugin,
135     /// from the plugin's thread.
plugin_connect(&self, plugin: Result<Plugin, io::Error>)136     pub fn plugin_connect(&self, plugin: Result<Plugin, io::Error>) {
137         if let Some(core) = self.upgrade() {
138             core.inner().plugin_connect(plugin)
139         }
140     }
141 
142     /// Called from a plugin runloop thread when the runloop exits.
plugin_exit(&self, plugin: PluginId, error: Result<(), ReadError>)143     pub fn plugin_exit(&self, plugin: PluginId, error: Result<(), ReadError>) {
144         if let Some(core) = self.upgrade() {
145             core.inner().plugin_exit(plugin, error)
146         }
147     }
148 
149     /// Handles the result of an update sent to a plugin.
150     ///
151     /// All plugins must acknowledge when they are sent a new update, so that
152     /// core can track which revisions are still 'live', that is can still
153     /// be the base revision for a delta. Once a plugin has acknowledged a new
154     /// revision, it can no longer send deltas against any older revision.
handle_plugin_update( &self, plugin: PluginId, view: ViewId, response: Result<Value, RpcError>, )155     pub fn handle_plugin_update(
156         &self,
157         plugin: PluginId,
158         view: ViewId,
159         response: Result<Value, RpcError>,
160     ) {
161         if let Some(core) = self.upgrade() {
162             let _t = xi_trace::trace_block("WeakXiCore::plugin_update", &["core"]);
163             core.inner().plugin_update(plugin, view, response);
164         }
165     }
166 }
167 
168 /// Handler for messages originating from plugins.
169 impl Handler for WeakXiCore {
170     type Notification = PluginCommand<PluginNotification>;
171     type Request = PluginCommand<PluginRequest>;
172 
handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification)173     fn handle_notification(&mut self, ctx: &RpcCtx, rpc: Self::Notification) {
174         let PluginCommand { view_id, plugin_id, cmd } = rpc;
175         if let Some(core) = self.upgrade() {
176             core.inner().plugin_notification(ctx, view_id, plugin_id, cmd)
177         }
178     }
179 
handle_request(&mut self, ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError>180     fn handle_request(&mut self, ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError> {
181         let PluginCommand { view_id, plugin_id, cmd } = rpc;
182         if let Some(core) = self.upgrade() {
183             core.inner().plugin_request(ctx, view_id, plugin_id, cmd)
184         } else {
185             Err(RemoteError::custom(0, "core is missing", None))
186         }
187     }
188 }
189 
190 #[cfg(test)]
191 /// Returns a non-functional `WeakXiRef`, needed to mock other types.
dummy_weak_core() -> WeakXiCore192 pub fn dummy_weak_core() -> WeakXiCore {
193     use xi_rpc::test_utils::DummyPeer;
194     use xi_rpc::Peer;
195     let peer = Box::new(DummyPeer);
196     let state = CoreState::new(&peer.box_clone(), None, None);
197     let core = Arc::new(Mutex::new(state));
198     WeakXiCore(Arc::downgrade(&core))
199 }
200