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