diff options
Diffstat (limited to 'cli/lsp/state.rs')
-rw-r--r-- | cli/lsp/state.rs | 109 |
1 files changed, 104 insertions, 5 deletions
diff --git a/cli/lsp/state.rs b/cli/lsp/state.rs index 18a1e4023..579a749f6 100644 --- a/cli/lsp/state.rs +++ b/cli/lsp/state.rs @@ -18,6 +18,9 @@ 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; @@ -25,11 +28,10 @@ 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::fs; use std::sync::Arc; use std::sync::RwLock; use std::time::Instant; @@ -37,6 +39,45 @@ 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), @@ -107,7 +148,7 @@ impl DocumentData { specifier: ModuleSpecifier, version: i32, source: &str, - maybe_import_map: Option<Rc<RefCell<ImportMap>>>, + maybe_import_map: Option<Arc<RwLock<ImportMap>>>, ) -> Self { let dependencies = if let Some((dependencies, _)) = analysis::analyze_dependencies( @@ -131,7 +172,7 @@ impl DocumentData { &mut self, version: i32, source: &str, - maybe_import_map: Option<Rc<RefCell<ImportMap>>>, + maybe_import_map: Option<Arc<RwLock<ImportMap>>>, ) { self.dependencies = if let Some((dependencies, _)) = analysis::analyze_dependencies( @@ -163,6 +204,8 @@ pub struct ServerState { 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>>, @@ -189,8 +232,10 @@ impl ServerState { Self { config, diagnostics: Default::default(), - doc_data: HashMap::new(), + 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)), @@ -290,3 +335,57 @@ impl ServerState { 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") + ) + ); + } +} |