1 // Copyright 2017 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 //! `PluginManager` handles launching, monitoring, and communicating with plugins. 16 17 use std::collections::{BTreeMap, BTreeSet, HashSet}; 18 use std::io; 19 use std::path::PathBuf; 20 use std::sync::{Arc, Mutex, Weak, MutexGuard}; 21 22 use std::path::Path; 23 use std::fmt::Debug; 24 25 use serde::Serialize; 26 use serde_json::{self, Value}; 27 28 use xi_rpc::{RpcCtx, Handler, RemoteError}; 29 use xi_trace::{self, trace_block, trace_block_payload}; 30 31 use tabs::{BufferIdentifier, ViewId, BufferContainerRef}; 32 use config::Table; 33 34 use super::{PluginCatalog, PluginRef, start_plugin_process, PluginPid}; 35 use super::rpc::{PluginNotification, PluginRequest, PluginCommand, 36 PluginUpdate, PluginBufferInfo, ClientPluginInfo}; 37 use super::manifest::{PluginActivation, Command}; 38 39 pub type PluginName = String; 40 type PluginGroup = BTreeMap<PluginName, PluginRef>; 41 42 /// Manages plugin loading, activation, lifecycle, and dispatch. 43 pub struct PluginManager { 44 catalog: PluginCatalog, 45 /// Buffer-scoped plugins, by buffer 46 buffer_plugins: BTreeMap<BufferIdentifier, PluginGroup>, 47 global_plugins: PluginGroup, 48 launching_globals: BTreeSet<PluginName>, 49 buffers: BufferContainerRef, 50 next_id: usize, 51 } 52 53 #[derive(Debug)] 54 /// The error type for plugin operations. 55 pub enum Error { 56 /// There was an error finding the buffer associated with a given view. 57 /// This probably means the buffer was destroyed while an RPC was in flight. 58 EditorMissing, 59 /// An error launching or communicating with a plugin process. 60 IoError(io::Error), 61 Other(String), 62 } 63 64 // Note: In general I have adopted the pattern of putting non-threadsafe 65 // API in the 'inner' type of types with a ___Ref variant. Methods on the 66 // Ref variant should be minimal, and provide a threadsafe interface. 67 68 impl PluginManager { 69 70 /// Returns plugins available to this view. get_available_plugins(&self, view_id: ViewId) -> Vec<ClientPluginInfo>71 pub fn get_available_plugins(&self, view_id: ViewId) -> Vec<ClientPluginInfo> { 72 self.catalog.iter_names().map(|name| { 73 let running = self.plugin_is_running(view_id, &name); 74 let name = name.clone(); 75 ClientPluginInfo { name, running } 76 }).collect::<Vec<_>>() 77 } 78 79 /// Passes an update from a buffer to all registered plugins. update_plugins(&mut self, view_id: ViewId, update: PluginUpdate, undo_group: usize) -> Result<(), Error>80 fn update_plugins(&mut self, view_id: ViewId, 81 update: PluginUpdate, undo_group: usize) -> Result<(), Error> { 82 let _t = trace_block("PluginManager::update_plugins", &["core"]); 83 84 // find all running plugins for this buffer, and send them the update 85 let mut dead_plugins = Vec::new(); 86 87 if let Ok(running) = self.running_for_view(view_id) { 88 for (name, plugin) in running.iter().chain(self.global_plugins.iter()) { 89 // check to see if plugins have crashed 90 if plugin.is_dead() { 91 dead_plugins.push((name.to_owned(), plugin.get_identifier())); 92 continue; 93 } 94 95 self.buffers.lock().editor_for_view_mut(view_id) 96 .unwrap().increment_revs_in_flight(); 97 98 let view_id = view_id.to_owned(); 99 let buffers = self.buffers.clone().to_weak(); 100 let mut plugin_ref = plugin.clone(); 101 102 plugin.update(&update, move |response| { 103 let buffers = match buffers.upgrade() { 104 Some(b) => b, 105 None => return, 106 }; 107 108 if response { 109 buffers.lock().editor_for_view_mut(view_id) 110 .unwrap().dec_revs_in_flight(); 111 } 112 }); 113 } 114 }; 115 self.cleanup_dead(view_id, &dead_plugins); 116 self.buffers.lock().editor_for_view_mut(view_id).unwrap().dec_revs_in_flight(); 117 Ok(()) 118 } 119 120 /// Sends a notification to groups of plugins. notify_plugins<V>(&self, view_id: ViewId, only_globals: bool, method: &str, params: &V) where V: Serialize + Debug121 fn notify_plugins<V>(&self, view_id: ViewId, 122 only_globals: bool, method: &str, params: &V) 123 where V: Serialize + Debug 124 { 125 let _t = trace_block("PluginManager::notify_plugins", &["core"]); 126 let params = serde_json::to_value(params) 127 .expect(&format!("bad notif params.\nmethod: {}\nparams: {:?}", 128 method, params)); 129 for plugin in self.global_plugins.values() { 130 plugin.rpc_notification(method, ¶ms); 131 } 132 if !only_globals { 133 if let Ok(locals) = self.running_for_view(view_id) { 134 for plugin in locals.values() { 135 plugin.rpc_notification(method, ¶ms); 136 } 137 } 138 } 139 } 140 toggle_tracing(&self, enabled: bool)141 fn toggle_tracing(&self, enabled: bool) { 142 self.global_plugins.values() 143 .for_each(|plug| { 144 plug.rpc_notification("tracing_config", 145 &json!({"enabled": enabled})) 146 }); 147 self.buffer_plugins.values().flat_map(|group| group.values()) 148 .for_each(|plug| { 149 plug.rpc_notification("tracing_config", 150 &json!({"enabled": enabled})) 151 }) 152 } 153 request_trace(&self) -> Vec<Value>154 fn request_trace(&self) -> Vec<Value> 155 { 156 let _t = trace_block("PluginManager::request_trace", &["core"]); 157 let mut gathered_results = Vec::new(); 158 159 for plugin in self.global_plugins.values() { 160 match plugin.request_traces() { 161 Ok(result) => gathered_results.push(result), 162 Err(e) => warn!("trace {:?}, {:?}", plugin.get_identifier(), e), 163 } 164 } 165 166 let mut processed_plugins = HashSet::new(); 167 168 for plugin in self.buffer_plugins.values().flat_map(|group| group.values()) { 169 // currently each buffer must have its own instance of a given plugin running. 170 assert!(processed_plugins.insert(plugin.get_identifier())); 171 match plugin.request_traces() { 172 Ok(result) => gathered_results.push(result), 173 Err(e) => warn!("trace {:?}, {:?}", plugin.get_identifier(), e), 174 } 175 } 176 177 gathered_results 178 } 179 dispatch_command(&self, view_id: ViewId, receiver: &str, method: &str, params: &Value)180 fn dispatch_command(&self, view_id: ViewId, receiver: &str, 181 method: &str, params: &Value) { 182 let plugin_ref = self.running_for_view(view_id) 183 .ok() 184 .and_then(|r| r.get(receiver)); 185 186 match plugin_ref { 187 Some(plug) => { 188 let inner = json!({"method": method, "params": params}); 189 plug.rpc_notification("custom_command", &inner); 190 } 191 None => { 192 error!("missing plugin {} for command {}", receiver, method); 193 } 194 } 195 } 196 197 /// Launches and initializes the named plugin. start_plugin(&mut self, self_ref: &PluginManagerRef, view_id: ViewId, init_info: &PluginBufferInfo, plugin_name: &str, ) -> Result<(), Error>198 fn start_plugin(&mut self, 199 self_ref: &PluginManagerRef, 200 view_id: ViewId, 201 init_info: &PluginBufferInfo, 202 plugin_name: &str, ) -> Result<(), Error> { 203 204 let _t = trace_block_payload("PluginManager::start_plugin", &["core"], 205 format!("{:?} {}", view_id, plugin_name)); 206 // verify that this view_id is valid 207 let _ = self.running_for_view(view_id)?; 208 if self.plugin_is_running(view_id, plugin_name) { 209 return Err(Error::Other(format!("{} already running", plugin_name))); 210 } 211 212 let plugin_id = self.next_plugin_id(); 213 let plugin_desc = self.catalog.get_named(plugin_name) 214 .ok_or(Error::Other(format!("no plugin found with name {}", plugin_name)))?; 215 216 let is_global = plugin_desc.is_global(); 217 if is_global && !self.launching_globals.insert(plugin_name.to_owned()) { 218 return Err(Error::Other(format!("global {} has started", plugin_name))) 219 } 220 221 let commands = plugin_desc.commands.clone(); 222 let init_info = if is_global { 223 let buffers = self.buffers.lock(); 224 let info = buffers.iter_editors() 225 .map(|ed| ed.plugin_init_info().to_owned()) 226 .collect::<Vec<_>>(); 227 info 228 } else { 229 vec![init_info.to_owned()] 230 }; 231 232 let me = self_ref.clone(); 233 let plugin_name = plugin_name.to_owned(); 234 235 start_plugin_process(self_ref, &plugin_desc, plugin_id, move |result| { 236 match result { 237 Ok(plugin_ref) => { 238 if xi_trace::is_enabled() { 239 plugin_ref.rpc_notification("tracing_config", 240 &json!({"enabled": true})); 241 } 242 plugin_ref.initialize(&init_info); 243 if is_global { 244 me.lock().on_plugin_connect_global(&plugin_name, plugin_ref, 245 commands); 246 } else { 247 me.lock().on_plugin_connect_local(view_id, &plugin_name, 248 plugin_ref, commands); 249 } 250 } 251 Err(err) => error!("failed to start plugin {}:\n {:?}", 252 plugin_name, err), 253 } 254 }); 255 Ok(()) 256 } 257 258 /// Callback used to register a successfully launched local plugin. on_plugin_connect_local(&mut self, view_id: ViewId, plugin_name: &str, plugin_ref: PluginRef, commands: Vec<Command>)259 fn on_plugin_connect_local(&mut self, view_id: ViewId, 260 plugin_name: &str, plugin_ref: PluginRef, 261 commands: Vec<Command>) { 262 // only add to our 'running' collection if the editor still exists 263 let is_running = match self.buffers.lock().editor_for_view(view_id) { 264 Some(ed) => { 265 ed.plugin_started(view_id, plugin_name, &commands); 266 true 267 } 268 None => false, 269 }; 270 if is_running { 271 let _ = self.running_for_view_mut(view_id) 272 .map(|running| running.insert(plugin_name.to_owned(), plugin_ref)); 273 } else { 274 error!("launch of plugin {} failed, no buffer for view {}", 275 plugin_name, view_id); 276 plugin_ref.shutdown(); 277 } 278 } 279 280 /// Callback used to register a successfully launched global plugin. on_plugin_connect_global(&mut self, plugin_name: &str, plugin_ref: PluginRef, commands: Vec<Command>)281 fn on_plugin_connect_global(&mut self, plugin_name: &str, 282 plugin_ref: PluginRef, commands: Vec<Command>) { 283 { 284 let buffers = self.buffers.lock(); 285 for ed in buffers.iter_editors() { 286 ed.plugin_started(None, plugin_name, &commands); 287 } 288 } 289 self.launching_globals.remove(plugin_name); 290 self.global_plugins.insert(plugin_name.to_owned(), plugin_ref); 291 } 292 stop_plugin(&mut self, view_id: ViewId, plugin_name: &str)293 fn stop_plugin(&mut self, view_id: ViewId, plugin_name: &str) { 294 let is_global = self.catalog.get_named(plugin_name).unwrap().is_global(); 295 if is_global { 296 let plugin_ref = self.global_plugins.remove(plugin_name); 297 if let Some(plugin_ref) = plugin_ref { 298 let plugin_id = plugin_ref.get_identifier(); 299 plugin_ref.shutdown(); 300 let mut buffers = self.buffers.lock(); 301 for ed in buffers.iter_editors_mut() { 302 ed.plugin_stopped(None, plugin_name, plugin_id, 0); 303 } 304 } 305 } 306 let plugin_ref = match self.running_for_view_mut(view_id) { 307 Ok(running) => running.remove(plugin_name), 308 Err(_) => None, 309 }; 310 311 if let Some(plugin_ref) = plugin_ref { 312 let plugin_id = plugin_ref.get_identifier(); 313 plugin_ref.shutdown(); 314 //TODO: should we notify now, or wait until we know this worked? 315 //can this fail? (yes.) How do we tell, and when do we kill the proc? 316 if let Some(ed) = self.buffers.lock().editor_for_view_mut(view_id) { 317 ed.plugin_stopped(view_id, plugin_name, plugin_id, 0); 318 } 319 } 320 } 321 322 /// Remove dead plugins, notifying editors as needed. 323 //TODO: this currently only runs after trying to update a plugin that has crashed 324 // during a previous update: that is, if a plugin crashes it isn't cleaned up 325 // immediately. If this is a problem, we should store crashes, and clean up in idle(). 326 #[allow(non_snake_case)] cleanup_dead(&mut self, view_id: ViewId, plugins: &[(PluginName, PluginPid)])327 fn cleanup_dead(&mut self, view_id: ViewId, plugins: &[(PluginName, PluginPid)]) { 328 //TODO: define exit codes, put them in an enum somewhere 329 let ABNORMAL_EXIT_CODE = 1; 330 for &(ref name, pid) in plugins.iter() { 331 let is_global = self.catalog.get_named(name).unwrap().is_global(); 332 if is_global { 333 { 334 self.global_plugins.remove(name); 335 } 336 let mut buffers = self.buffers.lock(); 337 for ed in buffers.iter_editors_mut() { 338 ed.plugin_stopped(None, name, pid, ABNORMAL_EXIT_CODE); 339 } 340 341 } else { 342 let _ = self.running_for_view_mut(view_id) 343 .map(|running| running.remove(name)); 344 self.buffers.lock().editor_for_view_mut(view_id).map(|ed|{ 345 ed.plugin_stopped(view_id, name, pid, ABNORMAL_EXIT_CODE); 346 }); 347 } 348 } 349 } 350 351 // ==================================================================== 352 // convenience functions 353 // ==================================================================== 354 buffer_for_view(&self, view_id: ViewId) -> Option<BufferIdentifier>355 fn buffer_for_view(&self, view_id: ViewId) -> Option<BufferIdentifier> { 356 self.buffers.buffer_for_view(view_id).map(|id| id.to_owned()) 357 } 358 next_plugin_id(&mut self) -> PluginPid359 fn next_plugin_id(&mut self) -> PluginPid { 360 self.next_id += 1; 361 PluginPid(self.next_id) 362 } 363 plugin_is_running(&self, view_id: ViewId, plugin_name: &str) -> bool364 fn plugin_is_running(&self, view_id: ViewId, plugin_name: &str) -> bool { 365 self.buffer_for_view(view_id) 366 .and_then(|id| self.buffer_plugins.get(&id)) 367 .map(|plugins| plugins.contains_key(plugin_name)) 368 .unwrap_or_default() || self.global_plugins.contains_key(plugin_name) 369 } 370 371 // TODO: we have a bunch of boilerplate around handling the rare case 372 // where we receive a command for some buffer which no longer exists. 373 // Maybe these two functions should return a Box<Iterator> of plugins, 374 // and if the buffer is missing just print a debug message and return 375 // an empty Iterator? running_for_view(&self, view_id: ViewId) -> Result<&PluginGroup, Error>376 fn running_for_view(&self, view_id: ViewId) -> Result<&PluginGroup, Error> { 377 self.buffer_for_view(view_id) 378 .and_then(|id| self.buffer_plugins.get(&id)) 379 .ok_or(Error::EditorMissing) 380 } 381 running_for_view_mut(&mut self, view_id: ViewId) -> Result<&mut PluginGroup, Error>382 fn running_for_view_mut(&mut self, view_id: ViewId) -> Result<&mut PluginGroup, Error> { 383 let buffer_id = match self.buffer_for_view(view_id) { 384 Some(id) => Ok(id), 385 None => Err(Error::EditorMissing), 386 }?; 387 self.buffer_plugins.get_mut(&buffer_id) 388 .ok_or(Error::EditorMissing) 389 } 390 } 391 392 /// Wrapper around an `Arc<Mutex<PluginManager>>`. 393 pub struct PluginManagerRef(Arc<Mutex<PluginManager>>); 394 395 impl Clone for PluginManagerRef { clone(&self) -> Self396 fn clone(&self) -> Self { 397 PluginManagerRef(self.0.clone()) 398 } 399 } 400 401 /// Wrapper around a `Weak<Mutex<PluginManager>>` 402 pub struct WeakPluginManagerRef(Weak<Mutex<PluginManager>>); 403 404 impl WeakPluginManagerRef { 405 /// Upgrades the weak reference to an Arc, if possible. 406 /// 407 /// Returns `None` if the inner value has been deallocated. upgrade(&self) -> Option<PluginManagerRef>408 pub fn upgrade(&self) -> Option<PluginManagerRef> { 409 match self.0.upgrade() { 410 Some(inner) => Some(PluginManagerRef(inner)), 411 None => None 412 } 413 } 414 } 415 416 impl PluginManagerRef { new(buffers: BufferContainerRef) -> Self417 pub fn new(buffers: BufferContainerRef) -> Self { 418 PluginManagerRef(Arc::new(Mutex::new( 419 PluginManager { 420 // TODO: actually parse these from manifest files 421 catalog: PluginCatalog::from_paths(Vec::new()), 422 buffer_plugins: BTreeMap::new(), 423 global_plugins: PluginGroup::new(), 424 launching_globals: BTreeSet::new(), 425 buffers, 426 next_id: 0, 427 } 428 ))) 429 } 430 lock(&self) -> MutexGuard<PluginManager>431 pub fn lock(&self) -> MutexGuard<PluginManager> { 432 self.0.lock().unwrap() 433 } 434 435 /// Creates a new `WeakPluginManagerRef`. to_weak(&self) -> WeakPluginManagerRef436 pub fn to_weak(&self) -> WeakPluginManagerRef { 437 WeakPluginManagerRef(Arc::downgrade(&self.0)) 438 } 439 set_plugin_search_path(&self, paths: Vec<PathBuf>)440 pub fn set_plugin_search_path(&self, paths: Vec<PathBuf>) { 441 // hacky: we don't handle unloading plugins if the path changes. 442 // this assumes that we only set the path once, when we get 443 // `client_init`. 444 let mut inner = self.lock(); 445 assert!(inner.catalog.iter().count() == 0); 446 inner.catalog = PluginCatalog::from_paths(paths); 447 448 } 449 toggle_tracing(&self, enabled: bool)450 pub fn toggle_tracing(&self, enabled: bool) { 451 self.lock().toggle_tracing(enabled) 452 } 453 collect_trace(&self) -> Vec<Value>454 pub fn collect_trace(&self) -> Vec<Value> { 455 self.lock().request_trace() 456 } 457 458 /// Called when a new buffer is created. document_new(&self, view_id: ViewId, init_info: &PluginBufferInfo)459 pub fn document_new(&self, view_id: ViewId, init_info: &PluginBufferInfo) { 460 let available = self.lock().get_available_plugins(view_id); 461 { 462 let inner = self.lock(); 463 let buffers = inner.buffers.lock(); 464 buffers.editor_for_view(view_id) 465 .map(|ed| { ed.available_plugins(view_id, &available) }); 466 } 467 468 if self.add_running_collection(view_id).is_ok() { 469 let to_start = self.activatable_plugins(view_id); 470 self.start_plugins(view_id, &init_info, &to_start); 471 self.lock().notify_plugins(view_id, true, "new_buffer", &json!({ 472 "buffer_info": vec![&init_info], 473 })); 474 } 475 } 476 477 /// Called when a buffer is saved to a file. document_did_save(&self, view_id: ViewId, path: &Path)478 pub fn document_did_save(&self, view_id: ViewId, path: &Path) { 479 self.lock().notify_plugins(view_id, false, "did_save", &json!({ 480 "view_id": view_id, 481 "path": path, 482 })); 483 } 484 485 /// Called when a buffer is closed. document_close(&self, view_id: ViewId)486 pub fn document_close(&self, view_id: ViewId) { 487 let to_stop = self.lock().running_for_view(view_id) 488 .map(|running| { 489 running.keys() 490 .map(|k| k.to_owned()) 491 .collect::<Vec<_>>() 492 }) 493 .unwrap_or_default(); 494 495 for plugin_name in to_stop { 496 self.stop_plugin(view_id, &plugin_name); 497 } 498 self.lock().notify_plugins(view_id, true, "did_close", &json!({ 499 "view_id": view_id})); 500 } 501 502 /// Called when a document's syntax definition has changed. document_syntax_changed(&self, view_id: ViewId, init_info: PluginBufferInfo)503 pub fn document_syntax_changed(&self, view_id: ViewId, init_info: PluginBufferInfo) { 504 info!("document_syntax_changed {}", view_id); 505 506 let start_keys = self.activatable_plugins(view_id).iter() 507 .map(|p| p.to_owned()) 508 .collect::<BTreeSet<_>>(); 509 510 // stop currently running plugins that aren't on list 511 // TODO: don't stop plugins that weren't started by a syntax activation 512 for plugin_name in self.lock().running_for_view(view_id) 513 .unwrap() 514 .keys() 515 .filter(|k| !start_keys.contains(*k)) { 516 self.stop_plugin(view_id, &plugin_name); 517 } 518 519 //TODO: send syntax_changed notification before starting new plugins 520 521 let to_run = start_keys.iter() 522 .filter(|k| !self.lock().plugin_is_running(view_id, k)) 523 .map(|k| k.clone().to_owned()) 524 .collect::<Vec<String>>(); 525 526 self.start_plugins(view_id, &init_info, &to_run); 527 } 528 529 /// Notifies plugins of a user config change document_config_changed(&self, view_id: ViewId, changes: &Table)530 pub fn document_config_changed(&self, view_id: ViewId, 531 changes: &Table) { 532 self.lock().notify_plugins(view_id, false, "config_changed", 533 &json!({"view_id": view_id, "changes": changes})); 534 } 535 536 /// Launches and initializes the named plugin. start_plugin(&self, view_id: ViewId, init_info: &PluginBufferInfo, plugin_name: &str) -> Result<(), Error>537 pub fn start_plugin(&self, 538 view_id: ViewId, 539 init_info: &PluginBufferInfo, 540 plugin_name: &str) -> Result<(), Error> { 541 self.lock().start_plugin(self, view_id, init_info, plugin_name) 542 } 543 544 /// Terminates and cleans up the named plugin. stop_plugin(&self, view_id: ViewId, plugin_name: &str)545 pub fn stop_plugin(&self, view_id: ViewId, plugin_name: &str) { 546 self.lock().stop_plugin(view_id, plugin_name); 547 } 548 549 /// Forward an update from a view to registered plugins. update_plugins(&self, view_id: ViewId, update: PluginUpdate, undo_group: usize) -> Result<(), Error>550 pub fn update_plugins(&self, view_id: ViewId, 551 update: PluginUpdate, undo_group: usize) -> Result<(), Error> { 552 self.lock().update_plugins(view_id, update, undo_group) 553 } 554 555 /// Sends a custom notification to a running plugin dispatch_command(&self, view_id: ViewId, receiver: &str, method: &str, params: &Value)556 pub fn dispatch_command(&self, view_id: ViewId, receiver: &str, 557 method: &str, params: &Value) { 558 self.lock().dispatch_command(view_id, receiver, method, params); 559 } 560 561 // ==================================================================== 562 // implementation details 563 // ==================================================================== 564 565 /// Performs new buffer setup. 566 /// 567 /// Returns an error if `view_id` does not have an associated buffer, 568 /// which is possible if it was closed immediately after creation. add_running_collection(&self, view_id: ViewId) -> Result<(),()>569 fn add_running_collection(&self, view_id: ViewId) -> Result<(),()> { 570 assert!(self.lock().running_for_view(view_id).is_err()); 571 let buf_id = self.lock().buffer_for_view(view_id); 572 match buf_id { 573 Some(buf_id) => { 574 self.lock().buffer_plugins.insert(buf_id, PluginGroup::new()); 575 Ok(()) 576 } 577 None => Err(()) 578 } 579 } 580 581 /// Returns the plugins which want to activate for this view. 582 /// 583 /// That a plugin wants to activate does not mean it will be activated. 584 /// For instance, it could have already been disabled by user preference. activatable_plugins(&self, view_id: ViewId) -> Vec<String>585 fn activatable_plugins(&self, view_id: ViewId) -> Vec<String> { 586 let inner = self.lock(); 587 let syntax = inner.buffers.lock() 588 .editor_for_view(view_id) 589 .unwrap() 590 .get_syntax() 591 .to_owned(); 592 593 inner.catalog.filter(|plug_desc|{ 594 plug_desc.activations.iter().any(|act|{ 595 match *act { 596 PluginActivation::Autorun => true, 597 PluginActivation::OnSyntax(ref other) if *other == syntax => true, 598 _ => false, 599 } 600 }) 601 }) 602 .iter() 603 .map(|desc| desc.name.to_owned()) 604 .collect::<Vec<_>>() 605 } 606 607 /// Batch run a group of plugins (as on creating a new view, for instance) start_plugins(&self, view_id: ViewId, init_info: &PluginBufferInfo, plugin_names: &Vec<String>)608 fn start_plugins(&self, view_id: ViewId, 609 init_info: &PluginBufferInfo, plugin_names: &Vec<String>) { 610 info!("starting plugins for {}", view_id); 611 for plugin_name in plugin_names.iter() { 612 match self.start_plugin(view_id, init_info, plugin_name) { 613 Ok(_) => info!("starting plugin {}", plugin_name), 614 Err(err) => error!("unable to start plugin {}, err: {:?}", 615 plugin_name, err), 616 } 617 } 618 } 619 } 620 621 impl Handler for PluginManagerRef { 622 type Notification = PluginCommand<PluginNotification>; 623 type Request = PluginCommand<PluginRequest>; 624 handle_notification(&mut self, _ctx: &RpcCtx, rpc: Self::Notification)625 fn handle_notification(&mut self, _ctx: &RpcCtx, rpc: Self::Notification) { 626 use self::PluginNotification::*; 627 let PluginCommand { view_id, plugin_id, cmd } = rpc; 628 let inner = self.lock(); 629 let mut buffers = inner.buffers.lock(); 630 631 match cmd { 632 AddScopes { scopes } => buffers.editor_for_view_mut(view_id) 633 .map(|ed| ed.plugin_add_scopes(plugin_id, scopes)), 634 UpdateSpans { start, len, spans, rev } => buffers.editor_for_view_mut(view_id) 635 .map(|ed| ed.plugin_update_spans(plugin_id, start, len, spans, rev)), 636 Edit { edit } => buffers.editor_for_view_mut(view_id) 637 .map(|ed| ed.plugin_edit_async(edit)), 638 Alert { msg } => buffers.editor_for_view(view_id) 639 .map(|ed| ed.plugin_alert(&msg)), 640 }; 641 } 642 handle_request(&mut self, _ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError>643 fn handle_request(&mut self, _ctx: &RpcCtx, rpc: Self::Request) -> Result<Value, RemoteError> { 644 use self::PluginRequest::*; 645 let PluginCommand { view_id, cmd, .. } = rpc; 646 let inner = self.lock(); 647 let buffers = inner.buffers.lock(); 648 649 let resp = match cmd { 650 LineCount => buffers.editor_for_view(view_id) 651 .map(|ed| json!(ed.plugin_n_lines())), 652 GetData { start, unit, max_size, rev } => buffers.editor_for_view(view_id) 653 .map(|ed| json!(ed.plugin_get_data(start, unit, max_size, rev))), 654 GetSelections => buffers.editor_for_view(view_id) 655 .map(|ed| json!(ed.plugin_get_selections(view_id))), 656 }; 657 resp.ok_or(RemoteError::custom(404, 658 "Missing editor", 659 json!({ 660 "view_id": view_id, 661 "rpc": &cmd 662 }))) 663 } 664 } 665