diff options
Diffstat (limited to 'cli/lsp/state.rs')
-rw-r--r-- | cli/lsp/state.rs | 395 |
1 files changed, 0 insertions, 395 deletions
diff --git a/cli/lsp/state.rs b/cli/lsp/state.rs deleted file mode 100644 index ceb4325a1..000000000 --- a/cli/lsp/state.rs +++ /dev/null @@ -1,395 +0,0 @@ -// 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::error::anyhow; -use deno_core::error::AnyError; -use deno_core::url::Url; -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::collections::HashMap; -use std::env; -use std::fmt; -use std::fs; -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 fn update_import_map(state: &mut ServerState) -> Result<(), AnyError> { - if let Some(import_map_str) = &state.config.settings.import_map { - let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str) { - Ok(url) - } else if let Some(root_uri) = &state.config.root_uri { - let root_path = root_uri - .to_file_path() - .map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?; - let import_map_path = root_path.join(import_map_str); - Url::from_file_path(import_map_path).map_err(|_| { - anyhow!("Bad file path for import map: {:?}", import_map_str) - }) - } else { - Err(anyhow!( - "The path to the import map (\"{}\") is not resolvable.", - import_map_str - )) - }?; - let import_map_path = import_map_url - .to_file_path() - .map_err(|_| anyhow!("Bad file path."))?; - let import_map_json = - fs::read_to_string(import_map_path).map_err(|err| { - anyhow!( - "Failed to load the import map at: {}. [{}]", - import_map_url, - err - ) - })?; - let import_map = - ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?; - state.maybe_import_map_uri = Some(import_map_url); - state.maybe_import_map = Some(Arc::new(RwLock::new(import_map))); - } else { - state.maybe_import_map = None; - } - Ok(()) -} - -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::<lsp_types::notification::DidOpenTextDocument>( - notification, - ) || notification_is::<lsp_types::notification::DidChangeTextDocument>( - 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<HashMap<String, analysis::Dependency>>, - pub version: Option<i32>, - specifier: ModuleSpecifier, -} - -impl DocumentData { - pub fn new( - specifier: ModuleSpecifier, - version: i32, - source: &str, - maybe_import_map: Option<Arc<RwLock<ImportMap>>>, - ) -> 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<Arc<RwLock<ImportMap>>>, - ) { - 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 assets: Arc<RwLock<HashMap<ModuleSpecifier, Option<String>>>>, - pub config: Config, - pub diagnostics: DiagnosticCollection, - pub doc_data: HashMap<ModuleSpecifier, DocumentData>, - pub file_cache: Arc<RwLock<MemoryCache>>, - pub sources: Arc<RwLock<Sources>>, -} - -pub struct ServerState { - pub assets: Arc<RwLock<HashMap<ModuleSpecifier, Option<String>>>>, - pub config: Config, - pub diagnostics: DiagnosticCollection, - pub doc_data: HashMap<ModuleSpecifier, DocumentData>, - pub file_cache: Arc<RwLock<MemoryCache>>, - pub maybe_import_map: Option<Arc<RwLock<ImportMap>>>, - pub maybe_import_map_uri: Option<Url>, - req_queue: ReqQueue, - sender: Sender<Message>, - pub sources: Arc<RwLock<Sources>>, - pub shutdown_requested: bool, - pub status: Status, - task_sender: Sender<Task>, - pub task_receiver: Receiver<Task>, - pub ts_runtime: JsRuntime, -} - -impl ServerState { - pub fn new(sender: Sender<Message>, 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 { - assets: Default::default(), - config, - diagnostics: Default::default(), - doc_data: Default::default(), - file_cache: Arc::new(RwLock::new(Default::default())), - maybe_import_map: None, - maybe_import_map_uri: None, - 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<Message>) -> Option<Event> { - 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<N: lsp_types::notification::Notification>( - &mut self, - params: N::Params, - ) { - let notification = Notification::new(N::METHOD.to_string(), params); - self.send(notification.into()); - } - - pub fn send_request<R: lsp_types::request::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 { - assets: Arc::clone(&self.assets), - 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<F>(&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; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use deno_core::serde_json::json; - use deno_core::serde_json::Value; - use lsp_server::Connection; - use tempfile::TempDir; - - #[test] - fn test_update_import_map() { - let temp_dir = TempDir::new().expect("could not create temp dir"); - let import_map_path = temp_dir.path().join("import_map.json"); - let import_map_str = &import_map_path.to_string_lossy(); - fs::write( - import_map_path.clone(), - r#"{ - "imports": { - "denoland/": "https://deno.land/x/" - } - }"#, - ) - .expect("could not write file"); - let mut config = Config::default(); - config - .update(json!({ - "enable": false, - "config": Value::Null, - "lint": false, - "importMap": import_map_str, - "unstable": true, - })) - .expect("could not update config"); - let (connection, _) = Connection::memory(); - let mut state = ServerState::new(connection.sender, config); - let result = update_import_map(&mut state); - assert!(result.is_ok()); - assert!(state.maybe_import_map.is_some()); - let expected = - Url::from_file_path(import_map_path).expect("could not parse url"); - assert_eq!(state.maybe_import_map_uri, Some(expected)); - let import_map = state.maybe_import_map.unwrap(); - let import_map = import_map.read().unwrap(); - assert_eq!( - import_map - .resolve("denoland/mod.ts", "https://example.com/index.js") - .expect("bad response"), - Some( - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts") - .expect("could not create URL") - ) - ); - } -} |