summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/diagnostics.rs8
-rw-r--r--cli/lsp/language_server.rs161
-rw-r--r--cli/lsp/tsc.rs18
3 files changed, 122 insertions, 65 deletions
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index e7be61cdc..96bb89d75 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -81,7 +81,7 @@ pub async fn generate_lint_diagnostics(
tokio::task::spawn_blocking(move || {
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() {
let file_id = file_cache.lookup(specifier).unwrap();
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() {
let file_id = {
// 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()
};
let version = doc_data.version;
@@ -266,8 +266,8 @@ pub async fn generate_dependency_diagnostics(
tokio::task::spawn_blocking(move || {
let mut diagnostics = Vec::new();
- let file_cache = state_snapshot.file_cache.read().unwrap();
- let mut sources = if let Ok(sources) = state_snapshot.sources.write() {
+ let file_cache = state_snapshot.file_cache.lock().unwrap();
+ let mut sources = if let Ok(sources) = state_snapshot.sources.lock() {
sources
} else {
return Err(custom_error("Deadlock", "deadlock locking sources"));
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 08ece2b62..09119cb36 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -17,12 +17,13 @@ use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use std::sync::Arc;
-use std::sync::RwLock;
+use std::sync::Mutex;
use tokio::fs;
use crate::deno_dir;
use crate::import_map::ImportMap;
use crate::media_type::MediaType;
+use crate::tsc_config::parse_config;
use crate::tsc_config::TsConfig;
use super::analysis;
@@ -42,24 +43,25 @@ use super::utils;
#[derive(Debug, Clone)]
pub struct LanguageServer {
- assets: Arc<RwLock<HashMap<ModuleSpecifier, Option<String>>>>,
+ assets: Arc<Mutex<HashMap<ModuleSpecifier, Option<String>>>>,
client: Client,
ts_server: TsServer,
- config: Arc<RwLock<Config>>,
- doc_data: Arc<RwLock<HashMap<ModuleSpecifier, DocumentData>>>,
- file_cache: Arc<RwLock<MemoryCache>>,
- sources: Arc<RwLock<Sources>>,
- diagnostics: Arc<RwLock<DiagnosticCollection>>,
- maybe_import_map: Arc<RwLock<Option<ImportMap>>>,
- maybe_import_map_uri: Arc<RwLock<Option<Url>>>,
+ config: Arc<Mutex<Config>>,
+ doc_data: Arc<Mutex<HashMap<ModuleSpecifier, DocumentData>>>,
+ file_cache: Arc<Mutex<MemoryCache>>,
+ sources: Arc<Mutex<Sources>>,
+ diagnostics: Arc<Mutex<DiagnosticCollection>>,
+ maybe_config_uri: Arc<Mutex<Option<Url>>>,
+ maybe_import_map: Arc<Mutex<Option<ImportMap>>>,
+ maybe_import_map_uri: Arc<Mutex<Option<Url>>>,
}
#[derive(Debug, Clone, Default)]
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 file_cache: Arc<RwLock<MemoryCache>>,
- pub sources: Arc<RwLock<Sources>>,
+ pub file_cache: Arc<Mutex<MemoryCache>>,
+ pub sources: Arc<Mutex<Sources>>,
}
impl LanguageServer {
@@ -68,7 +70,7 @@ impl LanguageServer {
let dir = deno_dir::DenoDir::new(maybe_custom_root)
.expect("could not access DENO_DIR");
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 {
assets: Default::default(),
@@ -79,13 +81,14 @@ impl LanguageServer {
file_cache: Default::default(),
sources,
diagnostics: Default::default(),
+ maybe_config_uri: Default::default(),
maybe_import_map: Default::default(),
maybe_import_map_uri: Default::default(),
}
}
fn enabled(&self) -> bool {
- let config = self.config.read().unwrap();
+ let config = self.config.lock().unwrap();
config.settings.enable
}
@@ -103,12 +106,12 @@ impl LanguageServer {
return Err(anyhow!("asset source missing: {}", specifier));
}
} 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) {
let file_text = file_cache.get_contents(file_id)?;
text::index_lines(&file_text)
} 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) {
line_index
} else {
@@ -121,20 +124,20 @@ impl LanguageServer {
async fn prepare_diagnostics(&self) -> Result<(), AnyError> {
let (enabled, lint_enabled) = {
- let config = self.config.read().unwrap();
+ let config = self.config.lock().unwrap();
(config.settings.enable, config.settings.lint)
};
let lint = async {
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(
self.snapshot(),
diagnostic_collection,
)
.await;
{
- let mut diagnostics_collection = self.diagnostics.write().unwrap();
+ let mut diagnostics_collection = self.diagnostics.lock().unwrap();
for (file_id, version, diagnostics) in diagnostics {
diagnostics_collection.set(
file_id,
@@ -153,7 +156,7 @@ impl LanguageServer {
let ts = async {
if enabled {
let diagnostics = {
- let diagnostic_collection = self.diagnostics.read().unwrap().clone();
+ let diagnostic_collection = self.diagnostics.lock().unwrap().clone();
match diagnostics::generate_ts_diagnostics(
&self.ts_server,
&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 {
diagnostics_collection.set(
file_id,
@@ -187,14 +190,14 @@ impl LanguageServer {
let deps = async {
if enabled {
- let diagnostics_collection = self.diagnostics.read().unwrap().clone();
+ let diagnostics_collection = self.diagnostics.lock().unwrap().clone();
let diagnostics = diagnostics::generate_dependency_diagnostics(
self.snapshot(),
diagnostics_collection,
)
.await?;
{
- let mut diagnostics_collection = self.diagnostics.write().unwrap();
+ let mut diagnostics_collection = self.diagnostics.lock().unwrap();
for (file_id, version, diagnostics) in diagnostics {
diagnostics_collection.set(
file_id,
@@ -220,12 +223,12 @@ impl LanguageServer {
async fn publish_diagnostics(&self) -> Result<(), AnyError> {
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();
(maybe_changes, diagnostics_collection.clone())
};
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 {
// TODO(@kitsonk) not totally happy with the way we collect and store
// different types of diagnostics and offer them up to the client, we
@@ -253,12 +256,12 @@ impl LanguageServer {
);
}
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()
};
let uri = specifier.as_url().clone();
let version = if let Some(doc_data) =
- self.doc_data.read().unwrap().get(&specifier)
+ self.doc_data.lock().unwrap().get(&specifier)
{
doc_data.version
} else {
@@ -277,7 +280,7 @@ impl LanguageServer {
pub fn snapshot(&self) -> StateSnapshot {
StateSnapshot {
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(),
sources: self.sources.clone(),
}
@@ -285,11 +288,11 @@ impl LanguageServer {
pub async fn update_import_map(&self) -> Result<(), AnyError> {
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())
};
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)
{
Ok(url)
@@ -320,10 +323,10 @@ impl LanguageServer {
})?;
let import_map =
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.write().unwrap() = Some(import_map);
+ *self.maybe_import_map_uri.lock().unwrap() = Some(import_map_url);
+ *self.maybe_import_map.lock().unwrap() = Some(import_map);
} else {
- *self.maybe_import_map.write().unwrap() = None;
+ *self.maybe_import_map.lock().unwrap() = None;
}
Ok(())
}
@@ -339,14 +342,56 @@ impl LanguageServer {
"strict": true,
"target": "esnext",
}));
- {
- let config = self.config.read().unwrap();
+ let (maybe_config, maybe_root_uri) = {
+ let config = self.config.lock().unwrap();
if config.settings.unstable {
let unstable_libs = json!({
"lib": ["deno.ns", "deno.window", "deno.unstable"]
});
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
.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;
if let Some(value) = params.initialization_options {
config.update(value)?;
@@ -451,10 +496,10 @@ impl lspower::LanguageServer for LanguageServer {
return;
}
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
.doc_data
- .write()
+ .lock()
.unwrap()
.insert(
specifier.clone(),
@@ -472,7 +517,7 @@ impl lspower::LanguageServer for LanguageServer {
self
.file_cache
- .write()
+ .lock()
.unwrap()
.set_contents(specifier, Some(params.text_document.text.into_bytes()));
// TODO(@lucacasonato): error handling
@@ -482,15 +527,15 @@ impl lspower::LanguageServer for LanguageServer {
async fn did_change(&self, params: DidChangeTextDocumentParams) {
let specifier = utils::normalize_url(params.text_document.uri);
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();
file_cache.get_contents(file_id).unwrap()
};
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 maybe_import_map = self.maybe_import_map.read().unwrap();
+ let maybe_import_map = self.maybe_import_map.lock().unwrap();
doc_data.update(
params.text_document.version,
&content,
@@ -500,7 +545,7 @@ impl lspower::LanguageServer for LanguageServer {
self
.file_cache
- .write()
+ .lock()
.unwrap()
.set_contents(specifier, Some(content.into_bytes()));
@@ -516,7 +561,7 @@ impl lspower::LanguageServer for LanguageServer {
return;
}
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);
}
// TODO(@kitsonk) should we do garbage collection on the diagnostics?
@@ -544,7 +589,7 @@ impl lspower::LanguageServer for LanguageServer {
match res {
Err(err) => error!("failed to fetch the extension settings {:?}", err),
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);
}
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
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 params.changes.iter().any(|fe| import_map_uri == fe.uri) {
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(
@@ -589,7 +646,7 @@ impl lspower::LanguageServer for LanguageServer {
) -> LspResult<Option<Vec<TextEdit>>> {
let specifier = utils::normalize_url(params.text_document.uri.clone());
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();
// TODO(lucacasonato): handle error properly
file_cache.get_contents(file_id).unwrap()
@@ -934,7 +991,7 @@ pub struct VirtualTextDocumentParams {
impl LanguageServer {
async fn cache(&self, params: CacheParams) -> LspResult<bool> {
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)
.await
.map_err(|err| {
@@ -942,9 +999,9 @@ impl LanguageServer {
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) {
- let mut diagnostics_collection = self.diagnostics.write().unwrap();
+ let mut diagnostics_collection = self.diagnostics.lock().unwrap();
diagnostics_collection.invalidate(&file_id);
}
}
@@ -962,7 +1019,7 @@ impl LanguageServer {
let specifier = utils::normalize_url(params.text_document.uri);
let url = specifier.as_url();
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!(
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) {
Some(text)
} else {
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 9de7ddcac..d40e18bbd 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -94,7 +94,7 @@ pub async fn get_asset(
Ok(Some(asset_text.to_string()))
} else {
{
- let assets = state_snapshot.assets.read().unwrap();
+ let assets = state_snapshot.assets.lock().unwrap();
if let Some(asset) = assets.get(specifier) {
return Ok(asset.clone());
}
@@ -107,7 +107,7 @@ pub async fn get_asset(
)
.await?,
)?;
- let mut assets = state_snapshot.assets.write().unwrap();
+ let mut assets = state_snapshot.assets.lock().unwrap();
assets.insert(specifier.clone(), asset.clone());
Ok(asset)
}
@@ -767,7 +767,7 @@ fn cache_snapshot(
{
let s = ModuleSpecifier::resolve_url(&specifier)?;
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();
file_cache.get_contents(file_id)?
};
@@ -863,7 +863,7 @@ fn get_length(state: &mut State, args: Value) -> Result<Value, AnyError> {
.unwrap();
Ok(json!(content.chars().count()))
} 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()))
}
}
@@ -888,7 +888,7 @@ fn get_text(state: &mut State, args: Value) -> Result<Value, AnyError> {
.unwrap()
.clone()
} else {
- let mut sources = state.state_snapshot.sources.write().unwrap();
+ let mut sources = state.state_snapshot.sources.lock().unwrap();
sources.get_text(&specifier).unwrap()
};
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 mut resolved = Vec::<Option<(String, String)>>::new();
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
} else {
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()));
}
} 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) {
return Ok(json!(version));
}
@@ -1256,7 +1256,7 @@ mod tests {
use crate::lsp::language_server::DocumentData;
use std::collections::HashMap;
use std::sync::Arc;
- use std::sync::RwLock;
+ use std::sync::Mutex;
fn mock_state_snapshot(sources: Vec<(&str, &str, i32)>) -> StateSnapshot {
let mut doc_data = HashMap::new();
@@ -1270,7 +1270,7 @@ mod tests {
);
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 {
assets: Default::default(),
doc_data,