1 // Copyright 2016 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 //! The main container for core state.
16 //!
17 //! All events from the frontend or from plugins are handled here first.
18 //!
19 //! This file is called 'tabs' for historical reasons, and should probably
20 //! be renamed.
21 
22 use std::cell::{Cell, RefCell};
23 use std::collections::{BTreeMap, HashSet};
24 use std::fmt;
25 use std::fs::File;
26 use std::io;
27 use std::mem;
28 use std::path::{Path, PathBuf};
29 
30 use serde::de::{self, Deserialize, Deserializer, Unexpected};
31 use serde::ser::{Serialize, Serializer};
32 use serde_json::Value;
33 
34 use xi_rope::Rope;
35 use xi_rpc::{self, ReadError, RemoteError, RpcCtx, RpcPeer};
36 use xi_trace::{self, trace_block};
37 
38 use crate::client::Client;
39 use crate::config::{self, ConfigDomain, ConfigDomainExternal, ConfigManager, Table};
40 use crate::editor::Editor;
41 use crate::event_context::EventContext;
42 use crate::file::FileManager;
43 use crate::line_ending::LineEnding;
44 use crate::plugin_rpc::{PluginNotification, PluginRequest};
45 use crate::plugins::rpc::ClientPluginInfo;
46 use crate::plugins::{start_plugin_process, Plugin, PluginCatalog, PluginPid};
47 use crate::recorder::Recorder;
48 use crate::rpc::{
49     CoreNotification, CoreRequest, EditNotification, EditRequest,
50     PluginNotification as CorePluginNotification,
51 };
52 use crate::styles::{ThemeStyleMap, DEFAULT_THEME};
53 use crate::syntax::LanguageId;
54 use crate::view::View;
55 use crate::whitespace::Indentation;
56 use crate::width_cache::WidthCache;
57 use crate::WeakXiCore;
58 
59 #[cfg(feature = "notify")]
60 use crate::watcher::{FileWatcher, WatchToken};
61 #[cfg(feature = "notify")]
62 use notify::DebouncedEvent;
63 #[cfg(feature = "notify")]
64 use std::ffi::OsStr;
65 
66 /// ViewIds are the primary means of routing messages between
67 /// xi-core and a client view.
68 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69 pub struct ViewId(pub(crate) usize);
70 
71 /// BufferIds uniquely identify open buffers.
72 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
73 pub struct BufferId(pub(crate) usize);
74 
75 pub type PluginId = crate::plugins::PluginPid;
76 
77 // old-style names; will be deprecated
78 pub type BufferIdentifier = BufferId;
79 
80 /// Totally arbitrary; we reserve this space for `ViewId`s
81 pub(crate) const RENDER_VIEW_IDLE_MASK: usize = 1 << 25;
82 pub(crate) const REWRAP_VIEW_IDLE_MASK: usize = 1 << 26;
83 pub(crate) const FIND_VIEW_IDLE_MASK: usize = 1 << 27;
84 
85 const NEW_VIEW_IDLE_TOKEN: usize = 1001;
86 
87 /// xi_rpc idle Token for watcher related idle scheduling.
88 pub(crate) const WATCH_IDLE_TOKEN: usize = 1002;
89 
90 #[cfg(feature = "notify")]
91 const CONFIG_EVENT_TOKEN: WatchToken = WatchToken(1);
92 
93 /// Token for file-change events in open files
94 #[cfg(feature = "notify")]
95 pub const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(2);
96 
97 #[cfg(feature = "notify")]
98 const THEME_FILE_EVENT_TOKEN: WatchToken = WatchToken(3);
99 
100 #[cfg(feature = "notify")]
101 const PLUGIN_EVENT_TOKEN: WatchToken = WatchToken(4);
102 
103 #[allow(dead_code)]
104 pub struct CoreState {
105     editors: BTreeMap<BufferId, RefCell<Editor>>,
106     views: BTreeMap<ViewId, RefCell<View>>,
107     file_manager: FileManager,
108     /// A local pasteboard.
109     kill_ring: RefCell<Rope>,
110     /// Theme and style state.
111     style_map: RefCell<ThemeStyleMap>,
112     width_cache: RefCell<WidthCache>,
113     /// User and platform specific settings
114     config_manager: ConfigManager,
115     /// Recorded editor actions
116     recorder: RefCell<Recorder>,
117     /// A weak reference to the main state container, stashed so that
118     /// it can be passed to plugins.
119     self_ref: Option<WeakXiCore>,
120     /// Views which need to have setup finished.
121     pending_views: Vec<(ViewId, Table)>,
122     peer: Client,
123     id_counter: Counter,
124     plugins: PluginCatalog,
125     // for the time being we auto-start all plugins we find on launch.
126     running_plugins: Vec<Plugin>,
127 }
128 
129 /// Initial setup and bookkeeping
130 impl CoreState {
new( peer: &RpcPeer, config_dir: Option<PathBuf>, extras_dir: Option<PathBuf>, ) -> Self131     pub(crate) fn new(
132         peer: &RpcPeer,
133         config_dir: Option<PathBuf>,
134         extras_dir: Option<PathBuf>,
135     ) -> Self {
136         #[cfg(feature = "notify")]
137         let mut watcher = FileWatcher::new(peer.clone());
138 
139         if let Some(p) = config_dir.as_ref() {
140             if !p.exists() {
141                 if let Err(e) = config::init_config_dir(p) {
142                     //TODO: report this error?
143                     error!("error initing file based configs: {:?}", e);
144                 }
145             }
146 
147             #[cfg(feature = "notify")]
148             watcher.watch_filtered(p, true, CONFIG_EVENT_TOKEN, |p| {
149                 p.extension().and_then(OsStr::to_str).unwrap_or("") == "xiconfig"
150             });
151         }
152 
153         let config_manager = ConfigManager::new(config_dir, extras_dir);
154 
155         let themes_dir = config_manager.get_themes_dir();
156         if let Some(p) = themes_dir.as_ref() {
157             #[cfg(feature = "notify")]
158             watcher.watch_filtered(p, true, THEME_FILE_EVENT_TOKEN, |p| {
159                 p.extension().and_then(OsStr::to_str).unwrap_or("") == "tmTheme"
160             });
161         }
162 
163         let plugins_dir = config_manager.get_plugins_dir();
164         if let Some(p) = plugins_dir.as_ref() {
165             #[cfg(feature = "notify")]
166             watcher.watch_filtered(p, true, PLUGIN_EVENT_TOKEN, |p| p.is_dir() || !p.exists());
167         }
168 
169         CoreState {
170             views: BTreeMap::new(),
171             editors: BTreeMap::new(),
172             #[cfg(feature = "notify")]
173             file_manager: FileManager::new(watcher),
174             #[cfg(not(feature = "notify"))]
175             file_manager: FileManager::new(),
176             kill_ring: RefCell::new(Rope::from("")),
177             style_map: RefCell::new(ThemeStyleMap::new(themes_dir)),
178             width_cache: RefCell::new(WidthCache::new()),
179             config_manager,
180             recorder: RefCell::new(Recorder::new()),
181             self_ref: None,
182             pending_views: Vec::new(),
183             peer: Client::new(peer.clone()),
184             id_counter: Counter::default(),
185             plugins: PluginCatalog::default(),
186             running_plugins: Vec::new(),
187         }
188     }
189 
next_view_id(&self) -> ViewId190     fn next_view_id(&self) -> ViewId {
191         ViewId(self.id_counter.next())
192     }
193 
next_buffer_id(&self) -> BufferId194     fn next_buffer_id(&self) -> BufferId {
195         BufferId(self.id_counter.next())
196     }
197 
next_plugin_id(&self) -> PluginId198     fn next_plugin_id(&self) -> PluginId {
199         PluginPid(self.id_counter.next())
200     }
201 
finish_setup(&mut self, self_ref: WeakXiCore)202     pub(crate) fn finish_setup(&mut self, self_ref: WeakXiCore) {
203         self.self_ref = Some(self_ref);
204 
205         if let Some(path) = self.config_manager.base_config_file_path() {
206             self.load_file_based_config(&path);
207         }
208 
209         // Load the custom theme files.
210         self.style_map.borrow_mut().load_theme_dir();
211 
212         // instead of having to do this here, config should just own
213         // the plugin catalog and reload automatically
214         let plugin_paths = self.config_manager.get_plugin_paths();
215         self.plugins.reload_from_paths(&plugin_paths);
216         let languages = self.plugins.make_languages_map();
217         let languages_ids = languages.iter().map(|l| l.name.clone()).collect::<Vec<_>>();
218         self.peer.available_languages(languages_ids);
219         self.config_manager.set_languages(languages);
220         let theme_names = self.style_map.borrow().get_theme_names();
221         self.peer.available_themes(theme_names);
222 
223         // FIXME: temporary: we just launch every plugin we find at startup
224         for manifest in self.plugins.iter() {
225             start_plugin_process(
226                 manifest.clone(),
227                 self.next_plugin_id(),
228                 self.self_ref.as_ref().unwrap().clone(),
229             );
230         }
231     }
232 
233     /// Attempt to load a config file.
load_file_based_config(&mut self, path: &Path)234     fn load_file_based_config(&mut self, path: &Path) {
235         let _t = trace_block("CoreState::load_config_file", &["core"]);
236         if let Some(domain) = self.config_manager.domain_for_path(path) {
237             match config::try_load_from_file(&path) {
238                 Ok(table) => self.set_config(domain, table),
239                 Err(e) => self.peer.alert(e.to_string()),
240             }
241         } else {
242             self.peer.alert(format!("Unexpected config file {:?}", path));
243         }
244     }
245 
246     /// Sets (overwriting) the config for a given domain.
set_config(&mut self, domain: ConfigDomain, table: Table)247     fn set_config(&mut self, domain: ConfigDomain, table: Table) {
248         match self.config_manager.set_user_config(domain, table) {
249             Err(e) => self.peer.alert(format!("{}", &e)),
250             Ok(changes) => self.handle_config_changes(changes),
251         }
252     }
253 
254     /// Notify editors/views/plugins of config changes.
handle_config_changes(&self, changes: Vec<(BufferId, Table)>)255     fn handle_config_changes(&self, changes: Vec<(BufferId, Table)>) {
256         for (id, table) in changes {
257             let view_id = self
258                 .views
259                 .values()
260                 .find(|v| v.borrow().get_buffer_id() == id)
261                 .map(|v| v.borrow().get_view_id())
262                 .unwrap();
263 
264             self.make_context(view_id).unwrap().config_changed(&table)
265         }
266     }
267 }
268 
269 /// Handling client events
270 impl CoreState {
271     /// Creates an `EventContext` for the provided `ViewId`. This context
272     /// holds references to the `Editor` and `View` backing this `ViewId`,
273     /// as well as to sibling views, plugins, and other state necessary
274     /// for handling most events.
make_context(&self, view_id: ViewId) -> Option<EventContext>275     pub(crate) fn make_context(&self, view_id: ViewId) -> Option<EventContext> {
276         self.views.get(&view_id).map(|view| {
277             let buffer_id = view.borrow().get_buffer_id();
278 
279             let editor = &self.editors[&buffer_id];
280             let info = self.file_manager.get_info(buffer_id);
281             let plugins = self.running_plugins.iter().collect::<Vec<_>>();
282             let config = self.config_manager.get_buffer_config(buffer_id);
283             let language = self.config_manager.get_buffer_language(buffer_id);
284 
285             EventContext {
286                 view_id,
287                 buffer_id,
288                 view,
289                 editor,
290                 config: &config.items,
291                 recorder: &self.recorder,
292                 language,
293                 info,
294                 siblings: Vec::new(),
295                 plugins,
296                 client: &self.peer,
297                 style_map: &self.style_map,
298                 width_cache: &self.width_cache,
299                 kill_ring: &self.kill_ring,
300                 weak_core: self.self_ref.as_ref().unwrap(),
301             }
302         })
303     }
304 
305     /// Produces an iterator over all event contexts, with each view appearing
306     /// exactly once.
iter_groups<'a>(&'a self) -> Iter<'a, Box<dyn Iterator<Item = &ViewId> + 'a>>307     fn iter_groups<'a>(&'a self) -> Iter<'a, Box<dyn Iterator<Item = &ViewId> + 'a>> {
308         Iter { views: Box::new(self.views.keys()), seen: HashSet::new(), inner: self }
309     }
310 
client_notification(&mut self, cmd: CoreNotification)311     pub(crate) fn client_notification(&mut self, cmd: CoreNotification) {
312         use self::CoreNotification::*;
313         use self::CorePluginNotification as PN;
314         match cmd {
315             Edit(crate::rpc::EditCommand { view_id, cmd }) => self.do_edit(view_id, cmd),
316             Save { view_id, file_path } => self.do_save(view_id, file_path),
317             CloseView { view_id } => self.do_close_view(view_id),
318             ModifyUserConfig { domain, changes } => self.do_modify_user_config(domain, changes),
319             SetTheme { theme_name } => self.do_set_theme(&theme_name),
320             SaveTrace { destination, frontend_samples } => {
321                 self.save_trace(&destination, frontend_samples)
322             }
323             Plugin(cmd) => match cmd {
324                 PN::Start { view_id, plugin_name } => self.do_start_plugin(view_id, &plugin_name),
325                 PN::Stop { view_id, plugin_name } => self.do_stop_plugin(view_id, &plugin_name),
326                 PN::PluginRpc { view_id, receiver, rpc } => {
327                     self.do_plugin_rpc(view_id, &receiver, &rpc.method, &rpc.params)
328                 }
329             },
330             TracingConfig { enabled } => self.toggle_tracing(enabled),
331             // handled at the top level
332             ClientStarted { .. } => (),
333             SetLanguage { view_id, language_id } => self.do_set_language(view_id, language_id),
334         }
335     }
336 
client_request(&mut self, cmd: CoreRequest) -> Result<Value, RemoteError>337     pub(crate) fn client_request(&mut self, cmd: CoreRequest) -> Result<Value, RemoteError> {
338         use self::CoreRequest::*;
339         match cmd {
340             //TODO: make file_path be an Option<PathBuf>
341             //TODO: make this a notification
342             NewView { file_path } => self.do_new_view(file_path.map(PathBuf::from)),
343             Edit(crate::rpc::EditCommand { view_id, cmd }) => self.do_edit_sync(view_id, cmd),
344             //TODO: why is this a request?? make a notification?
345             GetConfig { view_id } => self.do_get_config(view_id).map(|c| json!(c)),
346             DebugGetContents { view_id } => self.do_get_contents(view_id).map(|c| json!(c)),
347         }
348     }
349 
do_edit(&mut self, view_id: ViewId, cmd: EditNotification)350     fn do_edit(&mut self, view_id: ViewId, cmd: EditNotification) {
351         if let Some(mut edit_ctx) = self.make_context(view_id) {
352             edit_ctx.do_edit(cmd);
353         }
354     }
355 
do_edit_sync(&mut self, view_id: ViewId, cmd: EditRequest) -> Result<Value, RemoteError>356     fn do_edit_sync(&mut self, view_id: ViewId, cmd: EditRequest) -> Result<Value, RemoteError> {
357         if let Some(mut edit_ctx) = self.make_context(view_id) {
358             edit_ctx.do_edit_sync(cmd)
359         } else {
360             // TODO: some custom error tpye that can Into<RemoteError>
361             Err(RemoteError::custom(404, format!("missing view {:?}", view_id), None))
362         }
363     }
364 
do_new_view(&mut self, path: Option<PathBuf>) -> Result<Value, RemoteError>365     fn do_new_view(&mut self, path: Option<PathBuf>) -> Result<Value, RemoteError> {
366         let view_id = self.next_view_id();
367         let buffer_id = self.next_buffer_id();
368 
369         let rope = match path.as_ref() {
370             Some(p) => self.file_manager.open(p, buffer_id)?,
371             None => Rope::from(""),
372         };
373 
374         let editor = RefCell::new(Editor::with_text(rope));
375         let view = RefCell::new(View::new(view_id, buffer_id));
376 
377         self.editors.insert(buffer_id, editor);
378         self.views.insert(view_id, view);
379 
380         let config = self.config_manager.add_buffer(buffer_id, path.as_ref().map(|p| p.as_path()));
381 
382         //NOTE: because this is a synchronous call, we have to return the
383         //view_id before we can send any events to this view. We mark the
384         // view as pending and schedule the idle handler so that we can finish
385         // setting up this view on the next runloop pass, in finalize_new_views.
386         self.pending_views.push((view_id, config));
387         self.peer.schedule_idle(NEW_VIEW_IDLE_TOKEN);
388 
389         Ok(json!(view_id))
390     }
391 
do_save<P>(&mut self, view_id: ViewId, path: P) where P: AsRef<Path>,392     fn do_save<P>(&mut self, view_id: ViewId, path: P)
393     where
394         P: AsRef<Path>,
395     {
396         let _t = trace_block("CoreState::do_save", &["core"]);
397         let path = path.as_ref();
398         let buffer_id = self.views.get(&view_id).map(|v| v.borrow().get_buffer_id());
399         let buffer_id = match buffer_id {
400             Some(id) => id,
401             None => return,
402         };
403 
404         let mut save_ctx = self.make_context(view_id).unwrap();
405         let fin_text = save_ctx.text_for_save();
406 
407         if let Err(e) = self.file_manager.save(path, &fin_text, buffer_id) {
408             let error_message = e.to_string();
409             error!("File error: {:?}", error_message);
410             self.peer.alert(error_message);
411             return;
412         }
413 
414         let changes = self.config_manager.update_buffer_path(buffer_id, path);
415         let language = self.config_manager.get_buffer_language(buffer_id);
416 
417         self.make_context(view_id).unwrap().after_save(path);
418         self.make_context(view_id).unwrap().language_changed(&language);
419 
420         // update the config _after_ sending save related events
421         if let Some(changes) = changes {
422             self.make_context(view_id).unwrap().config_changed(&changes);
423         }
424     }
425 
do_close_view(&mut self, view_id: ViewId)426     fn do_close_view(&mut self, view_id: ViewId) {
427         let close_buffer = self.make_context(view_id).map(|ctx| ctx.close_view()).unwrap_or(true);
428 
429         let buffer_id = self.views.remove(&view_id).map(|v| v.borrow().get_buffer_id());
430 
431         if let Some(buffer_id) = buffer_id {
432             if close_buffer {
433                 self.editors.remove(&buffer_id);
434                 self.file_manager.close(buffer_id);
435                 self.config_manager.remove_buffer(buffer_id);
436             }
437         }
438     }
439 
do_set_theme(&self, theme_name: &str)440     fn do_set_theme(&self, theme_name: &str) {
441         //Set only if requested theme is different from the
442         //current one.
443         if theme_name != self.style_map.borrow().get_theme_name() {
444             if let Err(e) = self.style_map.borrow_mut().set_theme(&theme_name) {
445                 error!("error setting theme: {:?}, {:?}", theme_name, e);
446                 return;
447             }
448         }
449         self.notify_client_and_update_views();
450     }
451 
notify_client_and_update_views(&self)452     fn notify_client_and_update_views(&self) {
453         {
454             let style_map = self.style_map.borrow();
455             self.peer.theme_changed(style_map.get_theme_name(), style_map.get_theme_settings());
456         }
457 
458         self.iter_groups().for_each(|mut edit_ctx| {
459             edit_ctx.with_editor(|ed, view, _, _| {
460                 ed.theme_changed(&self.style_map.borrow());
461                 view.set_dirty(ed.get_buffer());
462             });
463             edit_ctx.render_if_needed();
464         });
465     }
466 
467     /// Updates the config for a given domain.
do_modify_user_config(&mut self, domain: ConfigDomainExternal, changes: Table)468     fn do_modify_user_config(&mut self, domain: ConfigDomainExternal, changes: Table) {
469         // the client sends ViewId but we need BufferId so we do a dance
470         let domain: ConfigDomain = match domain {
471             ConfigDomainExternal::General => ConfigDomain::General,
472             ConfigDomainExternal::Syntax(id) => ConfigDomain::Language(id),
473             ConfigDomainExternal::Language(id) => ConfigDomain::Language(id),
474             ConfigDomainExternal::UserOverride(view_id) => match self.views.get(&view_id) {
475                 Some(v) => ConfigDomain::UserOverride(v.borrow().get_buffer_id()),
476                 None => return,
477             },
478         };
479         let new_config = self.config_manager.table_for_update(domain.clone(), changes);
480         self.set_config(domain, new_config);
481     }
482 
do_get_config(&self, view_id: ViewId) -> Result<Table, RemoteError>483     fn do_get_config(&self, view_id: ViewId) -> Result<Table, RemoteError> {
484         let _t = trace_block("CoreState::get_config", &["core"]);
485         self.views
486             .get(&view_id)
487             .map(|v| v.borrow().get_buffer_id())
488             .map(|id| self.config_manager.get_buffer_config(id).to_table())
489             .ok_or(RemoteError::custom(404, format!("missing {}", view_id), None))
490     }
491 
do_get_contents(&self, view_id: ViewId) -> Result<Rope, RemoteError>492     fn do_get_contents(&self, view_id: ViewId) -> Result<Rope, RemoteError> {
493         self.make_context(view_id)
494             .map(|ctx| ctx.editor.borrow().get_buffer().to_owned())
495             .ok_or_else(|| RemoteError::custom(404, format!("No view for id {}", view_id), None))
496     }
497 
do_set_language(&mut self, view_id: ViewId, language_id: LanguageId)498     fn do_set_language(&mut self, view_id: ViewId, language_id: LanguageId) {
499         if let Some(view) = self.views.get(&view_id) {
500             let buffer_id = view.borrow().get_buffer_id();
501             let changes = self.config_manager.override_language(buffer_id, language_id.clone());
502 
503             let mut context = self.make_context(view_id).unwrap();
504             context.language_changed(&language_id);
505             if let Some(changes) = changes {
506                 context.config_changed(&changes);
507             }
508         }
509     }
510 
do_start_plugin(&mut self, _view_id: ViewId, plugin: &str)511     fn do_start_plugin(&mut self, _view_id: ViewId, plugin: &str) {
512         if self.running_plugins.iter().any(|p| p.name == plugin) {
513             info!("plugin {} already running", plugin);
514             return;
515         }
516 
517         if let Some(manifest) = self.plugins.get_named(plugin) {
518             //TODO: lots of races possible here, we need to keep track of
519             //pending launches.
520             start_plugin_process(
521                 manifest.clone(),
522                 self.next_plugin_id(),
523                 self.self_ref.as_ref().unwrap().clone(),
524             );
525         } else {
526             warn!("no plugin found with name '{}'", plugin);
527         }
528     }
529 
do_stop_plugin(&mut self, _view_id: ViewId, plugin: &str)530     fn do_stop_plugin(&mut self, _view_id: ViewId, plugin: &str) {
531         if let Some(p) = self
532             .running_plugins
533             .iter()
534             .position(|p| p.name == plugin)
535             .map(|ix| self.running_plugins.remove(ix))
536         {
537             //TODO: verify shutdown; kill if necessary
538             p.shutdown();
539             self.after_stop_plugin(&p);
540         }
541     }
542 
do_plugin_rpc(&self, view_id: ViewId, receiver: &str, method: &str, params: &Value)543     fn do_plugin_rpc(&self, view_id: ViewId, receiver: &str, method: &str, params: &Value) {
544         self.running_plugins
545             .iter()
546             .filter(|p| p.name == receiver)
547             .for_each(|p| p.dispatch_command(view_id, method, params))
548     }
549 
after_stop_plugin(&mut self, plugin: &Plugin)550     fn after_stop_plugin(&mut self, plugin: &Plugin) {
551         self.iter_groups().for_each(|mut cx| cx.plugin_stopped(plugin));
552     }
553 }
554 
555 /// Idle, tracing, and file event handling
556 impl CoreState {
handle_idle(&mut self, token: usize)557     pub(crate) fn handle_idle(&mut self, token: usize) {
558         match token {
559             NEW_VIEW_IDLE_TOKEN => self.finalize_new_views(),
560             WATCH_IDLE_TOKEN => self.handle_fs_events(),
561             other if (other & RENDER_VIEW_IDLE_MASK) != 0 => {
562                 self.handle_render_timer(other ^ RENDER_VIEW_IDLE_MASK)
563             }
564             other if (other & REWRAP_VIEW_IDLE_MASK) != 0 => {
565                 self.handle_rewrap_callback(other ^ REWRAP_VIEW_IDLE_MASK)
566             }
567             other if (other & FIND_VIEW_IDLE_MASK) != 0 => {
568                 self.handle_find_callback(other ^ FIND_VIEW_IDLE_MASK)
569             }
570             other => panic!("unexpected idle token {}", other),
571         };
572     }
573 
finalize_new_views(&mut self)574     fn finalize_new_views(&mut self) {
575         let to_start = mem::replace(&mut self.pending_views, Vec::new());
576         to_start.iter().for_each(|(id, config)| {
577             let modified = self.detect_whitespace(*id, config);
578             let config = modified.as_ref().unwrap_or(config);
579             let mut edit_ctx = self.make_context(*id).unwrap();
580             edit_ctx.finish_init(&config);
581         });
582     }
583 
584     // Detects whitespace settings from the file and merges them with the config
detect_whitespace(&mut self, id: ViewId, config: &Table) -> Option<Table>585     fn detect_whitespace(&mut self, id: ViewId, config: &Table) -> Option<Table> {
586         let buffer_id = self.views.get(&id).map(|v| v.borrow().get_buffer_id())?;
587         let editor = self
588             .editors
589             .get(&buffer_id)
590             .expect("existing buffer_id must have corresponding editor");
591 
592         if editor.borrow().get_buffer().is_empty() {
593             return None;
594         }
595 
596         let autodetect_whitespace =
597             self.config_manager.get_buffer_config(buffer_id).items.autodetect_whitespace;
598         if !autodetect_whitespace {
599             return None;
600         }
601 
602         let mut changes = Table::new();
603         let indentation = Indentation::parse(editor.borrow().get_buffer());
604         match indentation {
605             Ok(Some(Indentation::Tabs)) => {
606                 changes.insert("translate_tabs_to_spaces".into(), false.into());
607             }
608             Ok(Some(Indentation::Spaces(n))) => {
609                 changes.insert("translate_tabs_to_spaces".into(), true.into());
610                 changes.insert("tab_size".into(), n.into());
611             }
612             Err(_) => info!("detected mixed indentation"),
613             Ok(None) => info!("file contains no indentation"),
614         }
615 
616         let line_ending = LineEnding::parse(editor.borrow().get_buffer());
617         match line_ending {
618             Ok(Some(LineEnding::CrLf)) => {
619                 changes.insert("line_ending".into(), "\r\n".into());
620             }
621             Ok(Some(LineEnding::Lf)) => {
622                 changes.insert("line_ending".into(), "\n".into());
623             }
624             Err(_) => info!("detected mixed line endings"),
625             Ok(None) => info!("file contains no supported line endings"),
626         }
627 
628         let config_delta =
629             self.config_manager.table_for_update(ConfigDomain::SysOverride(buffer_id), changes);
630         match self
631             .config_manager
632             .set_user_config(ConfigDomain::SysOverride(buffer_id), config_delta)
633         {
634             Ok(ref mut items) if !items.is_empty() => {
635                 assert!(
636                     items.len() == 1,
637                     "whitespace overrides can only update a single buffer's config\n{:?}",
638                     items
639                 );
640                 let table = items.remove(0).1;
641                 let mut config = config.clone();
642                 config.extend(table);
643                 Some(config)
644             }
645             Ok(_) => {
646                 warn!("set_user_config failed to update config, no tables were returned");
647                 None
648             }
649             Err(err) => {
650                 warn!("detect_whitespace failed to update config: {:?}", err);
651                 None
652             }
653         }
654     }
655 
handle_render_timer(&mut self, token: usize)656     fn handle_render_timer(&mut self, token: usize) {
657         let id: ViewId = token.into();
658         if let Some(mut ctx) = self.make_context(id) {
659             ctx._finish_delayed_render();
660         }
661     }
662 
663     /// Callback for doing word wrap on a view
handle_rewrap_callback(&mut self, token: usize)664     fn handle_rewrap_callback(&mut self, token: usize) {
665         let id: ViewId = token.into();
666         if let Some(mut ctx) = self.make_context(id) {
667             ctx.do_rewrap_batch();
668         }
669     }
670 
671     /// Callback for doing incremental find in a view
handle_find_callback(&mut self, token: usize)672     fn handle_find_callback(&mut self, token: usize) {
673         let id: ViewId = token.into();
674         if let Some(mut ctx) = self.make_context(id) {
675             ctx.do_incremental_find();
676         }
677     }
678 
679     #[cfg(feature = "notify")]
handle_fs_events(&mut self)680     fn handle_fs_events(&mut self) {
681         let _t = trace_block("CoreState::handle_fs_events", &["core"]);
682         let mut events = self.file_manager.watcher().take_events();
683 
684         for (token, event) in events.drain(..) {
685             match token {
686                 OPEN_FILE_EVENT_TOKEN => self.handle_open_file_fs_event(event),
687                 CONFIG_EVENT_TOKEN => self.handle_config_fs_event(event),
688                 THEME_FILE_EVENT_TOKEN => self.handle_themes_fs_event(event),
689                 PLUGIN_EVENT_TOKEN => self.handle_plugin_fs_event(event),
690                 _ => warn!("unexpected fs event token {:?}", token),
691             }
692         }
693     }
694 
695     #[cfg(not(feature = "notify"))]
handle_fs_events(&mut self)696     fn handle_fs_events(&mut self) {}
697 
698     /// Handles a file system event related to a currently open file
699     #[cfg(feature = "notify")]
handle_open_file_fs_event(&mut self, event: DebouncedEvent)700     fn handle_open_file_fs_event(&mut self, event: DebouncedEvent) {
701         use notify::DebouncedEvent::*;
702         let path = match event {
703             NoticeWrite(ref path) | Create(ref path) | Write(ref path) | Chmod(ref path) => path,
704             other => {
705                 debug!("Event in open file {:?}", other);
706                 return;
707             }
708         };
709 
710         let buffer_id = match self.file_manager.get_editor(path) {
711             Some(id) => id,
712             None => return,
713         };
714 
715         let has_changes = self.file_manager.check_file(path, buffer_id);
716         let is_pristine = self.editors.get(&buffer_id).map(|ed| ed.borrow().is_pristine()).unwrap();
717         //TODO: currently we only use the file's modification time when
718         // determining if a file has been changed by another process.
719         // A more robust solution would also hash the file's contents.
720 
721         if has_changes && is_pristine {
722             if let Ok(text) = self.file_manager.open(path, buffer_id) {
723                 // this is ugly; we don't map buffer_id -> view_id anywhere
724                 // but we know we must have a view.
725                 let view_id = self
726                     .views
727                     .values()
728                     .find(|v| v.borrow().get_buffer_id() == buffer_id)
729                     .map(|v| v.borrow().get_view_id())
730                     .unwrap();
731                 self.make_context(view_id).unwrap().reload(text);
732             }
733         }
734     }
735 
736     /// Handles a config related file system event.
737     #[cfg(feature = "notify")]
handle_config_fs_event(&mut self, event: DebouncedEvent)738     fn handle_config_fs_event(&mut self, event: DebouncedEvent) {
739         use self::DebouncedEvent::*;
740         match event {
741             Create(ref path) | Write(ref path) | Chmod(ref path) => {
742                 self.load_file_based_config(path)
743             }
744             Remove(ref path) if !path.exists() => self.remove_config_at_path(path),
745             Rename(ref old, ref new) => {
746                 self.remove_config_at_path(old);
747                 self.load_file_based_config(new);
748             }
749             _ => (),
750         }
751     }
752 
remove_config_at_path(&mut self, path: &Path)753     fn remove_config_at_path(&mut self, path: &Path) {
754         if let Some(domain) = self.config_manager.domain_for_path(path) {
755             self.set_config(domain, Table::default());
756         }
757     }
758 
759     /// Handles changes in plugin files.
760     #[cfg(feature = "notify")]
handle_plugin_fs_event(&mut self, event: DebouncedEvent)761     fn handle_plugin_fs_event(&mut self, event: DebouncedEvent) {
762         use self::DebouncedEvent::*;
763         match event {
764             Create(ref path) | Write(ref path) => {
765                 self.plugins.load_from_paths(&[path.clone()]);
766                 if let Some(plugin) = self.plugins.get_from_path(path) {
767                     self.do_start_plugin(ViewId(0), &plugin.name);
768                 }
769             }
770             // the way FSEvents on macOS work, we want to verify that this path
771             // has actually be removed before we do anything.
772             NoticeRemove(ref path) | Remove(ref path) if !path.exists() => {
773                 if let Some(plugin) = self.plugins.get_from_path(path) {
774                     self.do_stop_plugin(ViewId(0), &plugin.name);
775                     self.plugins.remove_named(&plugin.name);
776                 }
777             }
778             Rename(ref old, ref new) => {
779                 if let Some(old_plugin) = self.plugins.get_from_path(old) {
780                     self.do_stop_plugin(ViewId(0), &old_plugin.name);
781                     self.plugins.remove_named(&old_plugin.name);
782                 }
783 
784                 self.plugins.load_from_paths(&[new.clone()]);
785                 if let Some(new_plugin) = self.plugins.get_from_path(new) {
786                     self.do_start_plugin(ViewId(0), &new_plugin.name);
787                 }
788             }
789             Chmod(ref path) | Remove(ref path) => {
790                 if let Some(plugin) = self.plugins.get_from_path(path) {
791                     self.do_stop_plugin(ViewId(0), &plugin.name);
792                     self.do_start_plugin(ViewId(0), &plugin.name);
793                 }
794             }
795             _ => (),
796         }
797 
798         self.views.keys().for_each(|view_id| {
799             let available_plugins = self
800                 .plugins
801                 .iter()
802                 .map(|plugin| ClientPluginInfo { name: plugin.name.clone(), running: true })
803                 .collect::<Vec<_>>();
804             self.peer.available_plugins(view_id.clone(), &available_plugins);
805         });
806     }
807 
808     /// Handles changes in theme files.
809     #[cfg(feature = "notify")]
handle_themes_fs_event(&mut self, event: DebouncedEvent)810     fn handle_themes_fs_event(&mut self, event: DebouncedEvent) {
811         use self::DebouncedEvent::*;
812         match event {
813             Create(ref path) | Write(ref path) => self.load_theme_file(path),
814             // the way FSEvents on macOS work, we want to verify that this path
815             // has actually be removed before we do anything.
816             NoticeRemove(ref path) | Remove(ref path) if !path.exists() => self.remove_theme(path),
817             Rename(ref old, ref new) => {
818                 self.remove_theme(old);
819                 self.load_theme_file(new);
820             }
821             Chmod(ref path) | Remove(ref path) => {
822                 self.style_map.borrow_mut().sync_dir(path.parent())
823             }
824             _ => (),
825         }
826         let theme_names = self.style_map.borrow().get_theme_names();
827         self.peer.available_themes(theme_names);
828     }
829 
830     /// Load a single theme file. Updates if already present.
load_theme_file(&mut self, path: &Path)831     fn load_theme_file(&mut self, path: &Path) {
832         let _t = trace_block("CoreState::load_theme_file", &["core"]);
833 
834         let result = self.style_map.borrow_mut().load_theme_info_from_path(path);
835         match result {
836             Ok(theme_name) => {
837                 if theme_name == self.style_map.borrow().get_theme_name() {
838                     if self.style_map.borrow_mut().set_theme(&theme_name).is_ok() {
839                         self.notify_client_and_update_views();
840                     }
841                 }
842             }
843             Err(e) => error!("Error loading theme file: {:?}, {:?}", path, e),
844         }
845     }
846 
remove_theme(&mut self, path: &Path)847     fn remove_theme(&mut self, path: &Path) {
848         let result = self.style_map.borrow_mut().remove_theme(path);
849 
850         // Set default theme if the removed theme was the
851         // current one.
852         if let Some(theme_name) = result {
853             if theme_name == self.style_map.borrow().get_theme_name() {
854                 self.do_set_theme(DEFAULT_THEME);
855             }
856         }
857     }
858 
toggle_tracing(&self, enabled: bool)859     fn toggle_tracing(&self, enabled: bool) {
860         self.running_plugins.iter().for_each(|plugin| plugin.toggle_tracing(enabled))
861     }
862 
save_trace<P>(&self, path: P, frontend_samples: Value) where P: AsRef<Path>,863     fn save_trace<P>(&self, path: P, frontend_samples: Value)
864     where
865         P: AsRef<Path>,
866     {
867         use xi_trace::chrome_trace_dump;
868         let mut all_traces = xi_trace::samples_cloned_unsorted();
869         if let Ok(mut traces) = chrome_trace_dump::decode(frontend_samples) {
870             all_traces.append(&mut traces);
871         }
872 
873         for plugin in &self.running_plugins {
874             match plugin.collect_trace() {
875                 Ok(json) => {
876                     let mut trace = chrome_trace_dump::decode(json).unwrap();
877                     all_traces.append(&mut trace);
878                 }
879                 Err(e) => error!("trace error {:?}", e),
880             }
881         }
882 
883         all_traces.sort_unstable();
884 
885         let mut trace_file = match File::create(path.as_ref()) {
886             Ok(f) => f,
887             Err(e) => {
888                 error!("error saving trace {:?}", e);
889                 return;
890             }
891         };
892 
893         if let Err(e) = chrome_trace_dump::serialize(&all_traces, &mut trace_file) {
894             error!("error saving trace {:?}", e);
895         }
896     }
897 }
898 
899 /// plugin event handling
900 impl CoreState {
901     /// Called from a plugin's thread after trying to start the plugin.
plugin_connect(&mut self, plugin: Result<Plugin, io::Error>)902     pub(crate) fn plugin_connect(&mut self, plugin: Result<Plugin, io::Error>) {
903         match plugin {
904             Ok(plugin) => {
905                 let init_info =
906                     self.iter_groups().map(|mut ctx| ctx.plugin_info()).collect::<Vec<_>>();
907                 plugin.initialize(init_info);
908                 self.iter_groups().for_each(|mut cx| cx.plugin_started(&plugin));
909                 self.running_plugins.push(plugin);
910             }
911             Err(e) => error!("failed to start plugin {:?}", e),
912         }
913     }
914 
plugin_exit(&mut self, id: PluginId, error: Result<(), ReadError>)915     pub(crate) fn plugin_exit(&mut self, id: PluginId, error: Result<(), ReadError>) {
916         warn!("plugin {:?} exited with result {:?}", id, error);
917         let running_idx = self.running_plugins.iter().position(|p| p.id == id);
918         if let Some(idx) = running_idx {
919             let plugin = self.running_plugins.remove(idx);
920             self.after_stop_plugin(&plugin);
921         }
922     }
923 
924     /// Handles the response to a sync update sent to a plugin.
plugin_update( &mut self, _plugin_id: PluginId, view_id: ViewId, response: Result<Value, xi_rpc::Error>, )925     pub(crate) fn plugin_update(
926         &mut self,
927         _plugin_id: PluginId,
928         view_id: ViewId,
929         response: Result<Value, xi_rpc::Error>,
930     ) {
931         if let Some(mut edit_ctx) = self.make_context(view_id) {
932             edit_ctx.do_plugin_update(response);
933         }
934     }
935 
plugin_notification( &mut self, _ctx: &RpcCtx, view_id: ViewId, plugin_id: PluginId, cmd: PluginNotification, )936     pub(crate) fn plugin_notification(
937         &mut self,
938         _ctx: &RpcCtx,
939         view_id: ViewId,
940         plugin_id: PluginId,
941         cmd: PluginNotification,
942     ) {
943         if let Some(mut edit_ctx) = self.make_context(view_id) {
944             edit_ctx.do_plugin_cmd(plugin_id, cmd)
945         }
946     }
947 
plugin_request( &mut self, _ctx: &RpcCtx, view_id: ViewId, plugin_id: PluginId, cmd: PluginRequest, ) -> Result<Value, RemoteError>948     pub(crate) fn plugin_request(
949         &mut self,
950         _ctx: &RpcCtx,
951         view_id: ViewId,
952         plugin_id: PluginId,
953         cmd: PluginRequest,
954     ) -> Result<Value, RemoteError> {
955         if let Some(mut edit_ctx) = self.make_context(view_id) {
956             Ok(edit_ctx.do_plugin_cmd_sync(plugin_id, cmd))
957         } else {
958             Err(RemoteError::custom(404, "missing view", None))
959         }
960     }
961 }
962 
963 /// test helpers
964 impl CoreState {
_test_open_editors(&self) -> Vec<BufferId>965     pub fn _test_open_editors(&self) -> Vec<BufferId> {
966         self.editors.keys().cloned().collect()
967     }
968 
_test_open_views(&self) -> Vec<ViewId>969     pub fn _test_open_views(&self) -> Vec<ViewId> {
970         self.views.keys().cloned().collect()
971     }
972 }
973 
974 pub mod test_helpers {
975     use super::{BufferId, ViewId};
976 
new_view_id(id: usize) -> ViewId977     pub fn new_view_id(id: usize) -> ViewId {
978         ViewId(id)
979     }
980 
new_buffer_id(id: usize) -> BufferId981     pub fn new_buffer_id(id: usize) -> BufferId {
982         BufferId(id)
983     }
984 }
985 
986 /// A multi-view aware iterator over `EventContext`s. A view which appears
987 /// as a sibling will not appear again as a main view.
988 pub struct Iter<'a, I> {
989     views: I,
990     seen: HashSet<ViewId>,
991     inner: &'a CoreState,
992 }
993 
994 impl<'a, I> Iterator for Iter<'a, I>
995 where
996     I: Iterator<Item = &'a ViewId>,
997 {
998     type Item = EventContext<'a>;
999 
next(&mut self) -> Option<Self::Item>1000     fn next(&mut self) -> Option<Self::Item> {
1001         let &mut Iter { ref mut views, ref mut seen, ref inner } = self;
1002         loop {
1003             let next_view = match views.next() {
1004                 None => return None,
1005                 Some(v) if seen.contains(v) => continue,
1006                 Some(v) => v,
1007             };
1008             let context = inner.make_context(*next_view).unwrap();
1009             context.siblings.iter().for_each(|sibl| {
1010                 let _ = seen.insert(sibl.borrow().get_view_id());
1011             });
1012             return Some(context);
1013         }
1014     }
1015 }
1016 
1017 #[derive(Debug, Default)]
1018 pub(crate) struct Counter(Cell<usize>);
1019 
1020 impl Counter {
next(&self) -> usize1021     pub(crate) fn next(&self) -> usize {
1022         let n = self.0.get();
1023         self.0.set(n + 1);
1024         n + 1
1025     }
1026 }
1027 
1028 // these two only exist so that we can use ViewIds as idle tokens
1029 impl From<usize> for ViewId {
from(src: usize) -> ViewId1030     fn from(src: usize) -> ViewId {
1031         ViewId(src)
1032     }
1033 }
1034 
1035 impl From<ViewId> for usize {
from(src: ViewId) -> usize1036     fn from(src: ViewId) -> usize {
1037         src.0
1038     }
1039 }
1040 
1041 impl fmt::Display for ViewId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1042     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1043         write!(f, "view-id-{}", self.0)
1044     }
1045 }
1046 
1047 impl Serialize for ViewId {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,1048     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1049     where
1050         S: Serializer,
1051     {
1052         serializer.serialize_str(&self.to_string())
1053     }
1054 }
1055 
1056 impl<'de> Deserialize<'de> for ViewId {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,1057     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1058     where
1059         D: Deserializer<'de>,
1060     {
1061         let s = String::deserialize(deserializer)?;
1062         let ord = s.trim_start_matches("view-id-");
1063         match usize::from_str_radix(ord, 10) {
1064             Ok(id) => Ok(ViewId(id)),
1065             Err(_) => Err(de::Error::invalid_value(Unexpected::Str(&s), &"view id")),
1066         }
1067     }
1068 }
1069 
1070 impl fmt::Display for BufferId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1071     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1072         write!(f, "buffer-id-{}", self.0)
1073     }
1074 }
1075 
1076 impl BufferId {
new(val: usize) -> Self1077     pub fn new(val: usize) -> Self {
1078         BufferId(val)
1079     }
1080 }
1081 
1082 #[cfg(test)]
1083 mod tests {
1084     use serde::Deserialize;
1085 
1086     use super::ViewId;
1087 
1088     #[test]
test_deserialize_view_id()1089     fn test_deserialize_view_id() {
1090         let de = json!("view-id-1");
1091         assert_eq!(ViewId::deserialize(&de).unwrap(), ViewId(1));
1092 
1093         let de = json!("not-a-view-id");
1094         assert!(ViewId::deserialize(&de).unwrap_err().is_data());
1095     }
1096 }
1097