// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::analysis; use super::config::Config; use super::diagnostics::DiagnosticCollection; use super::diagnostics::DiagnosticSource; use super::diagnostics::DiagnosticVec; use super::memory_cache::MemoryCache; use super::sources::Sources; use super::tsc; use super::utils::notification_is; use crate::deno_dir; use crate::import_map::ImportMap; use crate::media_type::MediaType; use crossbeam_channel::select; use crossbeam_channel::unbounded; use crossbeam_channel::Receiver; use crossbeam_channel::Sender; use deno_core::JsRuntime; use deno_core::ModuleSpecifier; use lsp_server::Message; use lsp_server::Notification; use lsp_server::Request; use lsp_server::RequestId; use lsp_server::Response; use std::cell::RefCell; use std::collections::HashMap; use std::env; use std::fmt; use std::rc::Rc; use std::sync::Arc; use std::sync::RwLock; use std::time::Instant; type ReqHandler = fn(&mut ServerState, Response); type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; pub enum Event { Message(Message), Task(Task), } impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let debug_verbose_not = |notification: &Notification, f: &mut fmt::Formatter| { f.debug_struct("Notification") .field("method", ¬ification.method) .finish() }; match self { Event::Message(Message::Notification(notification)) => { if notification_is::( notification, ) || notification_is::( notification, ) { return debug_verbose_not(notification, f); } } Event::Task(Task::Response(response)) => { return f .debug_struct("Response") .field("id", &response.id) .field("error", &response.error) .finish(); } _ => (), } match self { Event::Message(it) => fmt::Debug::fmt(it, f), Event::Task(it) => fmt::Debug::fmt(it, f), } } } #[derive(Eq, PartialEq, Copy, Clone)] pub enum Status { Loading, Ready, } impl Default for Status { fn default() -> Self { Status::Loading } } #[derive(Debug)] pub enum Task { Diagnostics((DiagnosticSource, DiagnosticVec)), Response(Response), } #[derive(Debug, Clone)] pub struct DocumentData { pub dependencies: Option>, pub version: Option, specifier: ModuleSpecifier, } impl DocumentData { pub fn new( specifier: ModuleSpecifier, version: i32, source: &str, maybe_import_map: Option>>, ) -> Self { let dependencies = if let Some((dependencies, _)) = analysis::analyze_dependencies( &specifier, source, &MediaType::from(&specifier), maybe_import_map, ) { Some(dependencies) } else { None }; Self { dependencies, version: Some(version), specifier, } } pub fn update( &mut self, version: i32, source: &str, maybe_import_map: Option>>, ) { self.dependencies = if let Some((dependencies, _)) = analysis::analyze_dependencies( &self.specifier, source, &MediaType::from(&self.specifier), maybe_import_map, ) { Some(dependencies) } else { None }; self.version = Some(version) } } /// An immutable snapshot of the server state at a point in time. #[derive(Debug, Clone, Default)] pub struct ServerStateSnapshot { pub config: Config, pub diagnostics: DiagnosticCollection, pub doc_data: HashMap, pub file_cache: Arc>, pub sources: Arc>, } pub struct ServerState { pub config: Config, pub diagnostics: DiagnosticCollection, pub doc_data: HashMap, pub file_cache: Arc>, req_queue: ReqQueue, sender: Sender, pub sources: Arc>, pub shutdown_requested: bool, pub status: Status, task_sender: Sender, pub task_receiver: Receiver, pub ts_runtime: JsRuntime, } impl ServerState { pub fn new(sender: Sender, config: Config) -> Self { let (task_sender, task_receiver) = unbounded(); let custom_root = env::var("DENO_DIR").map(String::into).ok(); let dir = deno_dir::DenoDir::new(custom_root).expect("could not access DENO_DIR"); let location = dir.root.join("deps"); let sources = Sources::new(&location); // TODO(@kitsonk) we need to allow displaying diagnostics here, but the // current compiler snapshot sends them to stdio which would totally break // the language server... let ts_runtime = tsc::start(false).expect("could not start tsc"); Self { config, diagnostics: Default::default(), doc_data: HashMap::new(), file_cache: Arc::new(RwLock::new(Default::default())), req_queue: Default::default(), sender, sources: Arc::new(RwLock::new(sources)), shutdown_requested: false, status: Default::default(), task_receiver, task_sender, ts_runtime, } } pub fn cancel(&mut self, request_id: RequestId) { if let Some(response) = self.req_queue.incoming.cancel(request_id) { self.send(response.into()); } } pub fn complete_request(&mut self, response: Response) { let handler = self.req_queue.outgoing.complete(response.id.clone()); handler(self, response) } pub fn next_event(&self, inbox: &Receiver) -> Option { select! { recv(inbox) -> msg => msg.ok().map(Event::Message), recv(self.task_receiver) -> task => Some(Event::Task(task.unwrap())), } } /// Handle any changes and return a `bool` that indicates if there were /// important changes to the state. pub fn process_changes(&mut self) -> bool { let mut file_cache = self.file_cache.write().unwrap(); let changed_files = file_cache.take_changes(); // other processing of changed files should be done here as needed !changed_files.is_empty() } pub fn register_request(&mut self, request: &Request, received: Instant) { self .req_queue .incoming .register(request.id.clone(), (request.method.clone(), received)); } pub fn respond(&mut self, response: Response) { if let Some((_, _)) = self.req_queue.incoming.complete(response.id.clone()) { self.send(response.into()); } } fn send(&mut self, message: Message) { self.sender.send(message).unwrap() } pub fn send_notification( &mut self, params: N::Params, ) { let notification = Notification::new(N::METHOD.to_string(), params); self.send(notification.into()); } pub fn send_request( &mut self, params: R::Params, handler: ReqHandler, ) { let request = self .req_queue .outgoing .register(R::METHOD.to_string(), params, handler); self.send(request.into()); } pub fn snapshot(&self) -> ServerStateSnapshot { ServerStateSnapshot { config: self.config.clone(), diagnostics: self.diagnostics.clone(), doc_data: self.doc_data.clone(), file_cache: Arc::clone(&self.file_cache), sources: Arc::clone(&self.sources), } } pub fn spawn(&mut self, task: F) where F: FnOnce() -> Task + Send + 'static, { let sender = self.task_sender.clone(); tokio::task::spawn_blocking(move || sender.send(task()).unwrap()); } pub fn transition(&mut self, new_status: Status) { self.status = new_status; } }