feat(lsp): support specifying a tsconfig file (#8926)

This commit is contained in:
Kitson Kelly 2020-12-31 14:33:44 +11:00 committed by GitHub
parent 0163cedd80
commit 587155f86d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 65 deletions

View file

@ -81,7 +81,7 @@ pub async fn generate_lint_diagnostics(
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut diagnostic_list = Vec::new(); let mut diagnostic_list = Vec::new();
let file_cache = state_snapshot.file_cache.read().unwrap(); let file_cache = state_snapshot.file_cache.lock().unwrap();
for (specifier, doc_data) in state_snapshot.doc_data.iter() { for (specifier, doc_data) in state_snapshot.doc_data.iter() {
let file_id = file_cache.lookup(specifier).unwrap(); let file_id = file_cache.lookup(specifier).unwrap();
let version = doc_data.version; let version = doc_data.version;
@ -242,7 +242,7 @@ pub async fn generate_ts_diagnostics(
for (specifier, doc_data) in state_snapshot_.doc_data.iter() { for (specifier, doc_data) in state_snapshot_.doc_data.iter() {
let file_id = { let file_id = {
// TODO(lucacasonato): this is highly inefficient // TODO(lucacasonato): this is highly inefficient
let file_cache = state_snapshot_.file_cache.read().unwrap(); let file_cache = state_snapshot_.file_cache.lock().unwrap();
file_cache.lookup(specifier).unwrap() file_cache.lookup(specifier).unwrap()
}; };
let version = doc_data.version; let version = doc_data.version;
@ -266,8 +266,8 @@ pub async fn generate_dependency_diagnostics(
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
let file_cache = state_snapshot.file_cache.read().unwrap(); let file_cache = state_snapshot.file_cache.lock().unwrap();
let mut sources = if let Ok(sources) = state_snapshot.sources.write() { let mut sources = if let Ok(sources) = state_snapshot.sources.lock() {
sources sources
} else { } else {
return Err(custom_error("Deadlock", "deadlock locking sources")); return Err(custom_error("Deadlock", "deadlock locking sources"));

View file

@ -17,12 +17,13 @@ use std::collections::HashMap;
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::Mutex;
use tokio::fs; use tokio::fs;
use crate::deno_dir; use crate::deno_dir;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use crate::tsc_config::parse_config;
use crate::tsc_config::TsConfig; use crate::tsc_config::TsConfig;
use super::analysis; use super::analysis;
@ -42,24 +43,25 @@ use super::utils;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LanguageServer { pub struct LanguageServer {
assets: Arc<RwLock<HashMap<ModuleSpecifier, Option<String>>>>, assets: Arc<Mutex<HashMap<ModuleSpecifier, Option<String>>>>,
client: Client, client: Client,
ts_server: TsServer, ts_server: TsServer,
config: Arc<RwLock<Config>>, config: Arc<Mutex<Config>>,
doc_data: Arc<RwLock<HashMap<ModuleSpecifier, DocumentData>>>, doc_data: Arc<Mutex<HashMap<ModuleSpecifier, DocumentData>>>,
file_cache: Arc<RwLock<MemoryCache>>, file_cache: Arc<Mutex<MemoryCache>>,
sources: Arc<RwLock<Sources>>, sources: Arc<Mutex<Sources>>,
diagnostics: Arc<RwLock<DiagnosticCollection>>, diagnostics: Arc<Mutex<DiagnosticCollection>>,
maybe_import_map: Arc<RwLock<Option<ImportMap>>>, maybe_config_uri: Arc<Mutex<Option<Url>>>,
maybe_import_map_uri: Arc<RwLock<Option<Url>>>, maybe_import_map: Arc<Mutex<Option<ImportMap>>>,
maybe_import_map_uri: Arc<Mutex<Option<Url>>>,
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct StateSnapshot { pub struct StateSnapshot {
pub assets: Arc<RwLock<HashMap<ModuleSpecifier, Option<String>>>>, pub assets: Arc<Mutex<HashMap<ModuleSpecifier, Option<String>>>>,
pub doc_data: HashMap<ModuleSpecifier, DocumentData>, pub doc_data: HashMap<ModuleSpecifier, DocumentData>,
pub file_cache: Arc<RwLock<MemoryCache>>, pub file_cache: Arc<Mutex<MemoryCache>>,
pub sources: Arc<RwLock<Sources>>, pub sources: Arc<Mutex<Sources>>,
} }
impl LanguageServer { impl LanguageServer {
@ -68,7 +70,7 @@ impl LanguageServer {
let dir = deno_dir::DenoDir::new(maybe_custom_root) let dir = deno_dir::DenoDir::new(maybe_custom_root)
.expect("could not access DENO_DIR"); .expect("could not access DENO_DIR");
let location = dir.root.join("deps"); let location = dir.root.join("deps");
let sources = Arc::new(RwLock::new(Sources::new(&location))); let sources = Arc::new(Mutex::new(Sources::new(&location)));
LanguageServer { LanguageServer {
assets: Default::default(), assets: Default::default(),
@ -79,13 +81,14 @@ impl LanguageServer {
file_cache: Default::default(), file_cache: Default::default(),
sources, sources,
diagnostics: Default::default(), diagnostics: Default::default(),
maybe_config_uri: Default::default(),
maybe_import_map: Default::default(), maybe_import_map: Default::default(),
maybe_import_map_uri: Default::default(), maybe_import_map_uri: Default::default(),
} }
} }
fn enabled(&self) -> bool { fn enabled(&self) -> bool {
let config = self.config.read().unwrap(); let config = self.config.lock().unwrap();
config.settings.enable config.settings.enable
} }
@ -103,12 +106,12 @@ impl LanguageServer {
return Err(anyhow!("asset source missing: {}", specifier)); return Err(anyhow!("asset source missing: {}", specifier));
} }
} else { } else {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
if let Some(file_id) = file_cache.lookup(&specifier) { if let Some(file_id) = file_cache.lookup(&specifier) {
let file_text = file_cache.get_contents(file_id)?; let file_text = file_cache.get_contents(file_id)?;
text::index_lines(&file_text) text::index_lines(&file_text)
} else { } else {
let mut sources = self.sources.write().unwrap(); let mut sources = self.sources.lock().unwrap();
if let Some(line_index) = sources.get_line_index(&specifier) { if let Some(line_index) = sources.get_line_index(&specifier) {
line_index line_index
} else { } else {
@ -121,20 +124,20 @@ impl LanguageServer {
async fn prepare_diagnostics(&self) -> Result<(), AnyError> { async fn prepare_diagnostics(&self) -> Result<(), AnyError> {
let (enabled, lint_enabled) = { let (enabled, lint_enabled) = {
let config = self.config.read().unwrap(); let config = self.config.lock().unwrap();
(config.settings.enable, config.settings.lint) (config.settings.enable, config.settings.lint)
}; };
let lint = async { let lint = async {
if lint_enabled { if lint_enabled {
let diagnostic_collection = self.diagnostics.read().unwrap().clone(); let diagnostic_collection = self.diagnostics.lock().unwrap().clone();
let diagnostics = diagnostics::generate_lint_diagnostics( let diagnostics = diagnostics::generate_lint_diagnostics(
self.snapshot(), self.snapshot(),
diagnostic_collection, diagnostic_collection,
) )
.await; .await;
{ {
let mut diagnostics_collection = self.diagnostics.write().unwrap(); let mut diagnostics_collection = self.diagnostics.lock().unwrap();
for (file_id, version, diagnostics) in diagnostics { for (file_id, version, diagnostics) in diagnostics {
diagnostics_collection.set( diagnostics_collection.set(
file_id, file_id,
@ -153,7 +156,7 @@ impl LanguageServer {
let ts = async { let ts = async {
if enabled { if enabled {
let diagnostics = { let diagnostics = {
let diagnostic_collection = self.diagnostics.read().unwrap().clone(); let diagnostic_collection = self.diagnostics.lock().unwrap().clone();
match diagnostics::generate_ts_diagnostics( match diagnostics::generate_ts_diagnostics(
&self.ts_server, &self.ts_server,
&diagnostic_collection, &diagnostic_collection,
@ -169,7 +172,7 @@ impl LanguageServer {
} }
}; };
{ {
let mut diagnostics_collection = self.diagnostics.write().unwrap(); let mut diagnostics_collection = self.diagnostics.lock().unwrap();
for (file_id, version, diagnostics) in diagnostics { for (file_id, version, diagnostics) in diagnostics {
diagnostics_collection.set( diagnostics_collection.set(
file_id, file_id,
@ -187,14 +190,14 @@ impl LanguageServer {
let deps = async { let deps = async {
if enabled { if enabled {
let diagnostics_collection = self.diagnostics.read().unwrap().clone(); let diagnostics_collection = self.diagnostics.lock().unwrap().clone();
let diagnostics = diagnostics::generate_dependency_diagnostics( let diagnostics = diagnostics::generate_dependency_diagnostics(
self.snapshot(), self.snapshot(),
diagnostics_collection, diagnostics_collection,
) )
.await?; .await?;
{ {
let mut diagnostics_collection = self.diagnostics.write().unwrap(); let mut diagnostics_collection = self.diagnostics.lock().unwrap();
for (file_id, version, diagnostics) in diagnostics { for (file_id, version, diagnostics) in diagnostics {
diagnostics_collection.set( diagnostics_collection.set(
file_id, file_id,
@ -220,12 +223,12 @@ impl LanguageServer {
async fn publish_diagnostics(&self) -> Result<(), AnyError> { async fn publish_diagnostics(&self) -> Result<(), AnyError> {
let (maybe_changes, diagnostics_collection) = { let (maybe_changes, diagnostics_collection) = {
let mut diagnostics_collection = self.diagnostics.write().unwrap(); let mut diagnostics_collection = self.diagnostics.lock().unwrap();
let maybe_changes = diagnostics_collection.take_changes(); let maybe_changes = diagnostics_collection.take_changes();
(maybe_changes, diagnostics_collection.clone()) (maybe_changes, diagnostics_collection.clone())
}; };
if let Some(diagnostic_changes) = maybe_changes { if let Some(diagnostic_changes) = maybe_changes {
let settings = self.config.read().unwrap().settings.clone(); let settings = self.config.lock().unwrap().settings.clone();
for file_id in diagnostic_changes { for file_id in diagnostic_changes {
// TODO(@kitsonk) not totally happy with the way we collect and store // TODO(@kitsonk) not totally happy with the way we collect and store
// different types of diagnostics and offer them up to the client, we // different types of diagnostics and offer them up to the client, we
@ -253,12 +256,12 @@ impl LanguageServer {
); );
} }
let specifier = { let specifier = {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
file_cache.get_specifier(file_id).clone() file_cache.get_specifier(file_id).clone()
}; };
let uri = specifier.as_url().clone(); let uri = specifier.as_url().clone();
let version = if let Some(doc_data) = let version = if let Some(doc_data) =
self.doc_data.read().unwrap().get(&specifier) self.doc_data.lock().unwrap().get(&specifier)
{ {
doc_data.version doc_data.version
} else { } else {
@ -277,7 +280,7 @@ impl LanguageServer {
pub fn snapshot(&self) -> StateSnapshot { pub fn snapshot(&self) -> StateSnapshot {
StateSnapshot { StateSnapshot {
assets: self.assets.clone(), assets: self.assets.clone(),
doc_data: self.doc_data.read().unwrap().clone(), doc_data: self.doc_data.lock().unwrap().clone(),
file_cache: self.file_cache.clone(), file_cache: self.file_cache.clone(),
sources: self.sources.clone(), sources: self.sources.clone(),
} }
@ -285,11 +288,11 @@ impl LanguageServer {
pub async fn update_import_map(&self) -> Result<(), AnyError> { pub async fn update_import_map(&self) -> Result<(), AnyError> {
let (maybe_import_map, maybe_root_uri) = { let (maybe_import_map, maybe_root_uri) = {
let config = self.config.read().unwrap(); let config = self.config.lock().unwrap();
(config.settings.import_map.clone(), config.root_uri.clone()) (config.settings.import_map.clone(), config.root_uri.clone())
}; };
if let Some(import_map_str) = &maybe_import_map { if let Some(import_map_str) = &maybe_import_map {
info!("update import map"); info!("Updating import map from: \"{}\"", import_map_str);
let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str) let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str)
{ {
Ok(url) Ok(url)
@ -320,10 +323,10 @@ impl LanguageServer {
})?; })?;
let import_map = let import_map =
ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?; ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?;
*self.maybe_import_map_uri.write().unwrap() = Some(import_map_url); *self.maybe_import_map_uri.lock().unwrap() = Some(import_map_url);
*self.maybe_import_map.write().unwrap() = Some(import_map); *self.maybe_import_map.lock().unwrap() = Some(import_map);
} else { } else {
*self.maybe_import_map.write().unwrap() = None; *self.maybe_import_map.lock().unwrap() = None;
} }
Ok(()) Ok(())
} }
@ -339,14 +342,56 @@ impl LanguageServer {
"strict": true, "strict": true,
"target": "esnext", "target": "esnext",
})); }));
{ let (maybe_config, maybe_root_uri) = {
let config = self.config.read().unwrap(); let config = self.config.lock().unwrap();
if config.settings.unstable { if config.settings.unstable {
let unstable_libs = json!({ let unstable_libs = json!({
"lib": ["deno.ns", "deno.window", "deno.unstable"] "lib": ["deno.ns", "deno.window", "deno.unstable"]
}); });
tsconfig.merge(&unstable_libs); tsconfig.merge(&unstable_libs);
} }
(config.settings.config.clone(), config.root_uri.clone())
};
if let Some(config_str) = &maybe_config {
info!("Updating TypeScript configuration from: \"{}\"", config_str);
let config_url = if let Ok(url) = Url::from_file_path(config_str) {
Ok(url)
} else if let Some(root_uri) = &maybe_root_uri {
let root_path = root_uri
.to_file_path()
.map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?;
let config_path = root_path.join(config_str);
Url::from_file_path(config_path).map_err(|_| {
anyhow!("Bad file path for configuration file: \"{}\"", config_str)
})
} else {
Err(anyhow!(
"The path to the configuration file (\"{}\") is not resolvable.",
config_str
))
}?;
let config_path = config_url
.to_file_path()
.map_err(|_| anyhow!("Bad file path."))?;
let config_text =
fs::read_to_string(config_path.clone())
.await
.map_err(|err| {
anyhow!(
"Failed to load the configuration file at: {}. [{}]",
config_url,
err
)
})?;
let (value, maybe_ignored_options) =
parse_config(&config_text, &config_path)?;
tsconfig.merge(&value);
*self.maybe_config_uri.lock().unwrap() = Some(config_url);
if let Some(ignored_options) = maybe_ignored_options {
// TODO(@kitsonk) turn these into diagnostics that can be sent to the
// client
warn!("{}", ignored_options);
}
} }
self self
.ts_server .ts_server
@ -388,7 +433,7 @@ impl lspower::LanguageServer for LanguageServer {
} }
{ {
let mut config = self.config.write().unwrap(); let mut config = self.config.lock().unwrap();
config.root_uri = params.root_uri; config.root_uri = params.root_uri;
if let Some(value) = params.initialization_options { if let Some(value) = params.initialization_options {
config.update(value)?; config.update(value)?;
@ -451,10 +496,10 @@ impl lspower::LanguageServer for LanguageServer {
return; return;
} }
let specifier = utils::normalize_url(params.text_document.uri); let specifier = utils::normalize_url(params.text_document.uri);
let maybe_import_map = self.maybe_import_map.read().unwrap().clone(); let maybe_import_map = self.maybe_import_map.lock().unwrap().clone();
if self if self
.doc_data .doc_data
.write() .lock()
.unwrap() .unwrap()
.insert( .insert(
specifier.clone(), specifier.clone(),
@ -472,7 +517,7 @@ impl lspower::LanguageServer for LanguageServer {
self self
.file_cache .file_cache
.write() .lock()
.unwrap() .unwrap()
.set_contents(specifier, Some(params.text_document.text.into_bytes())); .set_contents(specifier, Some(params.text_document.text.into_bytes()));
// TODO(@lucacasonato): error handling // TODO(@lucacasonato): error handling
@ -482,15 +527,15 @@ impl lspower::LanguageServer for LanguageServer {
async fn did_change(&self, params: DidChangeTextDocumentParams) { async fn did_change(&self, params: DidChangeTextDocumentParams) {
let specifier = utils::normalize_url(params.text_document.uri); let specifier = utils::normalize_url(params.text_document.uri);
let mut content = { let mut content = {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
let file_id = file_cache.lookup(&specifier).unwrap(); let file_id = file_cache.lookup(&specifier).unwrap();
file_cache.get_contents(file_id).unwrap() file_cache.get_contents(file_id).unwrap()
}; };
apply_content_changes(&mut content, params.content_changes); apply_content_changes(&mut content, params.content_changes);
{ {
let mut doc_data = self.doc_data.write().unwrap(); let mut doc_data = self.doc_data.lock().unwrap();
let doc_data = doc_data.get_mut(&specifier).unwrap(); let doc_data = doc_data.get_mut(&specifier).unwrap();
let maybe_import_map = self.maybe_import_map.read().unwrap(); let maybe_import_map = self.maybe_import_map.lock().unwrap();
doc_data.update( doc_data.update(
params.text_document.version, params.text_document.version,
&content, &content,
@ -500,7 +545,7 @@ impl lspower::LanguageServer for LanguageServer {
self self
.file_cache .file_cache
.write() .lock()
.unwrap() .unwrap()
.set_contents(specifier, Some(content.into_bytes())); .set_contents(specifier, Some(content.into_bytes()));
@ -516,7 +561,7 @@ impl lspower::LanguageServer for LanguageServer {
return; return;
} }
let specifier = utils::normalize_url(params.text_document.uri); let specifier = utils::normalize_url(params.text_document.uri);
if self.doc_data.write().unwrap().remove(&specifier).is_none() { if self.doc_data.lock().unwrap().remove(&specifier).is_none() {
error!("orphaned document: {}", specifier); error!("orphaned document: {}", specifier);
} }
// TODO(@kitsonk) should we do garbage collection on the diagnostics? // TODO(@kitsonk) should we do garbage collection on the diagnostics?
@ -544,7 +589,7 @@ impl lspower::LanguageServer for LanguageServer {
match res { match res {
Err(err) => error!("failed to fetch the extension settings {:?}", err), Err(err) => error!("failed to fetch the extension settings {:?}", err),
Ok(Some(config)) => { Ok(Some(config)) => {
if let Err(err) = self.config.write().unwrap().update(config) { if let Err(err) = self.config.lock().unwrap().update(config) {
error!("failed to update settings: {}", err); error!("failed to update settings: {}", err);
} }
if let Err(err) = self.update_import_map().await { if let Err(err) = self.update_import_map().await {
@ -570,7 +615,7 @@ impl lspower::LanguageServer for LanguageServer {
) { ) {
// if the current import map has changed, we need to reload it // if the current import map has changed, we need to reload it
let maybe_import_map_uri = let maybe_import_map_uri =
self.maybe_import_map_uri.read().unwrap().clone(); self.maybe_import_map_uri.lock().unwrap().clone();
if let Some(import_map_uri) = maybe_import_map_uri { if let Some(import_map_uri) = maybe_import_map_uri {
if params.changes.iter().any(|fe| import_map_uri == fe.uri) { if params.changes.iter().any(|fe| import_map_uri == fe.uri) {
if let Err(err) = self.update_import_map().await { if let Err(err) = self.update_import_map().await {
@ -581,6 +626,18 @@ impl lspower::LanguageServer for LanguageServer {
} }
} }
} }
// if the current tsconfig has changed, we need to reload it
let maybe_config_uri = self.maybe_config_uri.lock().unwrap().clone();
if let Some(config_uri) = maybe_config_uri {
if params.changes.iter().any(|fe| config_uri == fe.uri) {
if let Err(err) = self.update_tsconfig().await {
self
.client
.show_message(MessageType::Warning, err.to_string())
.await;
}
}
}
} }
async fn formatting( async fn formatting(
@ -589,7 +646,7 @@ impl lspower::LanguageServer for LanguageServer {
) -> LspResult<Option<Vec<TextEdit>>> { ) -> LspResult<Option<Vec<TextEdit>>> {
let specifier = utils::normalize_url(params.text_document.uri.clone()); let specifier = utils::normalize_url(params.text_document.uri.clone());
let file_text = { let file_text = {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
let file_id = file_cache.lookup(&specifier).unwrap(); let file_id = file_cache.lookup(&specifier).unwrap();
// TODO(lucacasonato): handle error properly // TODO(lucacasonato): handle error properly
file_cache.get_contents(file_id).unwrap() file_cache.get_contents(file_id).unwrap()
@ -934,7 +991,7 @@ pub struct VirtualTextDocumentParams {
impl LanguageServer { impl LanguageServer {
async fn cache(&self, params: CacheParams) -> LspResult<bool> { async fn cache(&self, params: CacheParams) -> LspResult<bool> {
let specifier = utils::normalize_url(params.text_document.uri); let specifier = utils::normalize_url(params.text_document.uri);
let maybe_import_map = self.maybe_import_map.read().unwrap().clone(); let maybe_import_map = self.maybe_import_map.lock().unwrap().clone();
sources::cache(specifier.clone(), maybe_import_map) sources::cache(specifier.clone(), maybe_import_map)
.await .await
.map_err(|err| { .map_err(|err| {
@ -942,9 +999,9 @@ impl LanguageServer {
LspError::internal_error() LspError::internal_error()
})?; })?;
{ {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
if let Some(file_id) = file_cache.lookup(&specifier) { if let Some(file_id) = file_cache.lookup(&specifier) {
let mut diagnostics_collection = self.diagnostics.write().unwrap(); let mut diagnostics_collection = self.diagnostics.lock().unwrap();
diagnostics_collection.invalidate(&file_id); diagnostics_collection.invalidate(&file_id);
} }
} }
@ -962,7 +1019,7 @@ impl LanguageServer {
let specifier = utils::normalize_url(params.text_document.uri); let specifier = utils::normalize_url(params.text_document.uri);
let url = specifier.as_url(); let url = specifier.as_url();
let contents = if url.as_str() == "deno:/status.md" { let contents = if url.as_str() == "deno:/status.md" {
let file_cache = self.file_cache.read().unwrap(); let file_cache = self.file_cache.lock().unwrap();
Some(format!( Some(format!(
r#"# Deno Language Server Status r#"# Deno Language Server Status
@ -987,7 +1044,7 @@ impl LanguageServer {
} }
} }
_ => { _ => {
let mut sources = self.sources.write().unwrap(); let mut sources = self.sources.lock().unwrap();
if let Some(text) = sources.get_text(&specifier) { if let Some(text) = sources.get_text(&specifier) {
Some(text) Some(text)
} else { } else {

View file

@ -94,7 +94,7 @@ pub async fn get_asset(
Ok(Some(asset_text.to_string())) Ok(Some(asset_text.to_string()))
} else { } else {
{ {
let assets = state_snapshot.assets.read().unwrap(); let assets = state_snapshot.assets.lock().unwrap();
if let Some(asset) = assets.get(specifier) { if let Some(asset) = assets.get(specifier) {
return Ok(asset.clone()); return Ok(asset.clone());
} }
@ -107,7 +107,7 @@ pub async fn get_asset(
) )
.await?, .await?,
)?; )?;
let mut assets = state_snapshot.assets.write().unwrap(); let mut assets = state_snapshot.assets.lock().unwrap();
assets.insert(specifier.clone(), asset.clone()); assets.insert(specifier.clone(), asset.clone());
Ok(asset) Ok(asset)
} }
@ -767,7 +767,7 @@ fn cache_snapshot(
{ {
let s = ModuleSpecifier::resolve_url(&specifier)?; let s = ModuleSpecifier::resolve_url(&specifier)?;
let content = { let content = {
let file_cache = state.state_snapshot.file_cache.read().unwrap(); let file_cache = state.state_snapshot.file_cache.lock().unwrap();
let file_id = file_cache.lookup(&s).unwrap(); let file_id = file_cache.lookup(&s).unwrap();
file_cache.get_contents(file_id)? file_cache.get_contents(file_id)?
}; };
@ -863,7 +863,7 @@ fn get_length(state: &mut State, args: Value) -> Result<Value, AnyError> {
.unwrap(); .unwrap();
Ok(json!(content.chars().count())) Ok(json!(content.chars().count()))
} else { } else {
let mut sources = state.state_snapshot.sources.write().unwrap(); let mut sources = state.state_snapshot.sources.lock().unwrap();
Ok(json!(sources.get_length(&specifier).unwrap())) Ok(json!(sources.get_length(&specifier).unwrap()))
} }
} }
@ -888,7 +888,7 @@ fn get_text(state: &mut State, args: Value) -> Result<Value, AnyError> {
.unwrap() .unwrap()
.clone() .clone()
} else { } else {
let mut sources = state.state_snapshot.sources.write().unwrap(); let mut sources = state.state_snapshot.sources.lock().unwrap();
sources.get_text(&specifier).unwrap() sources.get_text(&specifier).unwrap()
}; };
Ok(json!(text::slice(&content, v.start..v.end))) Ok(json!(text::slice(&content, v.start..v.end)))
@ -898,7 +898,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
let v: ResolveArgs = serde_json::from_value(args)?; let v: ResolveArgs = serde_json::from_value(args)?;
let mut resolved = Vec::<Option<(String, String)>>::new(); let mut resolved = Vec::<Option<(String, String)>>::new();
let referrer = ModuleSpecifier::resolve_url(&v.base)?; let referrer = ModuleSpecifier::resolve_url(&v.base)?;
let mut sources = if let Ok(sources) = state.state_snapshot.sources.write() { let mut sources = if let Ok(sources) = state.state_snapshot.sources.lock() {
sources sources
} else { } else {
return Err(custom_error("Deadlock", "deadlock locking sources")); return Err(custom_error("Deadlock", "deadlock locking sources"));
@ -999,7 +999,7 @@ fn script_version(state: &mut State, args: Value) -> Result<Value, AnyError> {
return Ok(json!(version.to_string())); return Ok(json!(version.to_string()));
} }
} else { } else {
let mut sources = state.state_snapshot.sources.write().unwrap(); let mut sources = state.state_snapshot.sources.lock().unwrap();
if let Some(version) = sources.get_script_version(&specifier) { if let Some(version) = sources.get_script_version(&specifier) {
return Ok(json!(version)); return Ok(json!(version));
} }
@ -1256,7 +1256,7 @@ mod tests {
use crate::lsp::language_server::DocumentData; use crate::lsp::language_server::DocumentData;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::Mutex;
fn mock_state_snapshot(sources: Vec<(&str, &str, i32)>) -> StateSnapshot { fn mock_state_snapshot(sources: Vec<(&str, &str, i32)>) -> StateSnapshot {
let mut doc_data = HashMap::new(); let mut doc_data = HashMap::new();
@ -1270,7 +1270,7 @@ mod tests {
); );
file_cache.set_contents(specifier, Some(content.as_bytes().to_vec())); file_cache.set_contents(specifier, Some(content.as_bytes().to_vec()));
} }
let file_cache = Arc::new(RwLock::new(file_cache)); let file_cache = Arc::new(Mutex::new(file_cache));
StateSnapshot { StateSnapshot {
assets: Default::default(), assets: Default::default(),
doc_data, doc_data,