1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00

fix(lsp): retain module dependencies when parse is invalid (#12782)

Fixes #12753
This commit is contained in:
Kitson Kelly 2021-11-17 09:23:25 +11:00 committed by GitHub
parent fd78953e1c
commit cc38580106
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 80 deletions

View file

@ -492,19 +492,17 @@ async fn generate_deps_diagnostics(
.get_version(document.specifier(), &DiagnosticSource::Deno); .get_version(document.specifier(), &DiagnosticSource::Deno);
if version != current_version { if version != current_version {
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
if let Some(dependencies) = document.dependencies() { for (_, dependency) in document.dependencies() {
for (_, dependency) in dependencies { diagnose_dependency(
diagnose_dependency( &mut diagnostics,
&mut diagnostics, &documents,
&documents, &dependency.maybe_code,
&dependency.maybe_code, );
); diagnose_dependency(
diagnose_dependency( &mut diagnostics,
&mut diagnostics, &documents,
&documents, &dependency.maybe_type,
&dependency.maybe_type, );
);
}
} }
diagnostics_vec.push(( diagnostics_vec.push((
document.specifier().clone(), document.specifier().clone(),

View file

@ -245,6 +245,8 @@ impl SyntheticModule {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct DocumentInner { struct DocumentInner {
/// contains the last-known-good set of dependencies from parsing the module
dependencies: Arc<BTreeMap<String, deno_graph::Dependency>>,
fs_version: String, fs_version: String,
line_index: Arc<LineIndex>, line_index: Arc<LineIndex>,
maybe_language_id: Option<LanguageId>, maybe_language_id: Option<LanguageId>,
@ -282,9 +284,15 @@ impl Document {
maybe_resolver, maybe_resolver,
Some(&parser), Some(&parser),
)); ));
let dependencies = if let Some(Ok(module)) = &maybe_module {
Arc::new(module.dependencies.clone())
} else {
Arc::new(BTreeMap::new())
};
let text_info = SourceTextInfo::new(content); let text_info = SourceTextInfo::new(content);
let line_index = Arc::new(LineIndex::new(text_info.text_str())); let line_index = Arc::new(LineIndex::new(text_info.text_str()));
Self(Arc::new(DocumentInner { Self(Arc::new(DocumentInner {
dependencies,
fs_version, fs_version,
line_index, line_index,
maybe_language_id: None, maybe_language_id: None,
@ -317,9 +325,15 @@ impl Document {
} else { } else {
None None
}; };
let dependencies = if let Some(Ok(module)) = &maybe_module {
Arc::new(module.dependencies.clone())
} else {
Arc::new(BTreeMap::new())
};
let source = SourceTextInfo::new(content); let source = SourceTextInfo::new(content);
let line_index = Arc::new(LineIndex::new(source.text_str())); let line_index = Arc::new(LineIndex::new(source.text_str()));
Self(Arc::new(DocumentInner { Self(Arc::new(DocumentInner {
dependencies,
fs_version: "1".to_string(), fs_version: "1".to_string(),
line_index, line_index,
maybe_language_id: Some(language_id), maybe_language_id: Some(language_id),
@ -379,14 +393,20 @@ impl Document {
} else { } else {
None None
}; };
let source = SourceTextInfo::new(content); let dependencies = if let Some(Ok(module)) = &maybe_module {
Arc::new(module.dependencies.clone())
} else {
self.0.dependencies.clone()
};
let text_info = SourceTextInfo::new(content);
let line_index = if index_valid == IndexValid::All { let line_index = if index_valid == IndexValid::All {
line_index line_index
} else { } else {
Arc::new(LineIndex::new(source.text_str())) Arc::new(LineIndex::new(text_info.text_str()))
}; };
Ok(Document(Arc::new(DocumentInner { Ok(Document(Arc::new(DocumentInner {
text_info: source, dependencies,
text_info,
line_index, line_index,
maybe_module, maybe_module,
maybe_lsp_version: Some(version), maybe_lsp_version: Some(version),
@ -499,15 +519,13 @@ impl Document {
self.0.maybe_warning.clone() self.0.maybe_warning.clone()
} }
pub fn dependencies(&self) -> Option<Vec<(String, deno_graph::Dependency)>> { pub fn dependencies(&self) -> Vec<(String, deno_graph::Dependency)> {
let module = self.maybe_module()?.as_ref().ok()?; self
Some( .0
module .dependencies
.dependencies .iter()
.iter() .map(|(s, d)| (s.clone(), d.clone()))
.map(|(s, d)| (s.clone(), d.clone())) .collect()
.collect(),
)
} }
/// If the supplied position is within a dependency range, return the resolved /// If the supplied position is within a dependency range, return the resolved
@ -911,35 +929,32 @@ impl DocumentsInner {
specifiers: Vec<String>, specifiers: Vec<String>,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> { ) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> {
let doc = self.get(referrer)?; let dependencies = self.get(referrer)?.0.dependencies.clone();
let mut results = Vec::new(); let mut results = Vec::new();
if let Some(Ok(module)) = doc.maybe_module() { for specifier in specifiers {
let dependencies = module.dependencies.clone(); if specifier.starts_with("asset:") {
for specifier in specifiers { if let Ok(specifier) = ModuleSpecifier::parse(&specifier) {
if specifier.starts_with("asset:") { let media_type = MediaType::from(&specifier);
if let Ok(specifier) = ModuleSpecifier::parse(&specifier) { results.push(Some((specifier, media_type)));
let media_type = MediaType::from(&specifier);
results.push(Some((specifier, media_type)));
} else {
results.push(None);
}
} else if let Some(dep) = dependencies.get(&specifier) {
if let Some(Ok((specifier, _))) = &dep.maybe_type {
results.push(self.resolve_dependency(specifier));
} else if let Some(Ok((specifier, _))) = &dep.maybe_code {
results.push(self.resolve_dependency(specifier));
} else {
results.push(None);
}
} else if let Some(Some(Ok((specifier, _)))) =
self.resolve_imports_dependency(&specifier)
{
// clone here to avoid double borrow of self
let specifier = specifier.clone();
results.push(self.resolve_dependency(&specifier));
} else { } else {
results.push(None); results.push(None);
} }
} else if let Some(dep) = dependencies.get(&specifier) {
if let Some(Ok((specifier, _))) = &dep.maybe_type {
results.push(self.resolve_dependency(specifier));
} else if let Some(Ok((specifier, _))) = &dep.maybe_code {
results.push(self.resolve_dependency(specifier));
} else {
results.push(None);
}
} else if let Some(Some(Ok((specifier, _)))) =
self.resolve_imports_dependency(&specifier)
{
// clone here to avoid double borrow of self
let specifier = specifier.clone();
results.push(self.resolve_dependency(&specifier));
} else {
results.push(None);
} }
} }
Some(results) Some(results)

View file

@ -987,17 +987,16 @@ impl Inner {
let asset_or_document = self.get_cached_asset_or_document(&specifier)?; let asset_or_document = self.get_cached_asset_or_document(&specifier)?;
let line_index = asset_or_document.line_index(); let line_index = asset_or_document.line_index();
let req = tsc::RequestMethod::GetNavigationTree(specifier); let navigation_tree =
let navigation_tree: tsc::NavigationTree = self self.get_navigation_tree(&specifier).await.map_err(|err| {
.ts_server error!(
.request(self.snapshot()?, req) "Error getting document symbols for \"{}\": {}",
.await specifier, err
.map_err(|err| { );
error!("Failed to request to tsserver {}", err); LspError::internal_error()
LspError::invalid_request()
})?; })?;
let response = if let Some(child_items) = navigation_tree.child_items { let response = if let Some(child_items) = &navigation_tree.child_items {
let mut document_symbols = Vec::<DocumentSymbol>::new(); let mut document_symbols = Vec::<DocumentSymbol>::new();
for item in child_items { for item in child_items {
item item
@ -1543,7 +1542,7 @@ impl Inner {
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
let line_index = asset_or_doc.line_index(); let line_index = asset_or_doc.line_index();
let req = tsc::RequestMethod::GetReferences(( let req = tsc::RequestMethod::GetReferences((
specifier, specifier.clone(),
line_index.offset_tsc(params.text_document_position.position)?, line_index.offset_tsc(params.text_document_position.position)?,
)); ));
let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self
@ -1563,9 +1562,14 @@ impl Inner {
} }
let reference_specifier = let reference_specifier =
resolve_url(&reference.document_span.file_name).unwrap(); resolve_url(&reference.document_span.file_name).unwrap();
let asset_or_doc = let reference_line_index = if reference_specifier == specifier {
self.get_asset_or_document(&reference_specifier).await?; line_index.clone()
results.push(reference.to_location(asset_or_doc.line_index(), self)); } else {
let asset_or_doc =
self.get_asset_or_document(&reference_specifier).await?;
asset_or_doc.line_index()
};
results.push(reference.to_location(reference_line_index, self));
} }
self.performance.measure(mark); self.performance.measure(mark);
@ -2157,8 +2161,8 @@ impl Inner {
LspError::invalid_request() LspError::invalid_request()
})?; })?;
let semantic_tokens: SemanticTokens = let semantic_tokens =
semantic_classification.to_semantic_tokens(line_index); semantic_classification.to_semantic_tokens(line_index)?;
let response = if !semantic_tokens.data.is_empty() { let response = if !semantic_tokens.data.is_empty() {
Some(SemanticTokensResult::Tokens(semantic_tokens)) Some(SemanticTokensResult::Tokens(semantic_tokens))
} else { } else {
@ -2200,8 +2204,8 @@ impl Inner {
LspError::invalid_request() LspError::invalid_request()
})?; })?;
let semantic_tokens: SemanticTokens = let semantic_tokens =
semantic_classification.to_semantic_tokens(line_index); semantic_classification.to_semantic_tokens(line_index)?;
let response = if !semantic_tokens.data.is_empty() { let response = if !semantic_tokens.data.is_empty() {
Some(SemanticTokensRangeResult::Tokens(semantic_tokens)) Some(SemanticTokensRangeResult::Tokens(semantic_tokens))
} else { } else {

View file

@ -38,6 +38,8 @@ use deno_core::OpFn;
use deno_core::RuntimeOptions; use deno_core::RuntimeOptions;
use deno_runtime::tokio_util::create_basic_runtime; use deno_runtime::tokio_util::create_basic_runtime;
use log::warn; use log::warn;
use lspower::jsonrpc::Error as LspError;
use lspower::jsonrpc::Result as LspResult;
use lspower::lsp; use lspower::lsp;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -1122,7 +1124,7 @@ impl Classifications {
pub fn to_semantic_tokens( pub fn to_semantic_tokens(
&self, &self,
line_index: Arc<LineIndex>, line_index: Arc<LineIndex>,
) -> lsp::SemanticTokens { ) -> LspResult<lsp::SemanticTokens> {
let token_count = self.spans.len() / 3; let token_count = self.spans.len() / 3;
let mut builder = SemanticTokensBuilder::new(); let mut builder = SemanticTokensBuilder::new();
for i in 0..token_count { for i in 0..token_count {
@ -1141,16 +1143,26 @@ impl Classifications {
let start_pos = line_index.position_tsc(offset.into()); let start_pos = line_index.position_tsc(offset.into());
let end_pos = line_index.position_tsc(TextSize::from(offset + length)); let end_pos = line_index.position_tsc(TextSize::from(offset + length));
// start_pos.line == end_pos.line is always true as there are no multiline tokens if start_pos.line == end_pos.line
builder.push( && start_pos.character <= end_pos.character
start_pos.line, {
start_pos.character, builder.push(
end_pos.character - start_pos.character, start_pos.line,
token_type, start_pos.character,
token_modifiers, end_pos.character - start_pos.character,
); token_type,
token_modifiers,
);
} else {
log::error!(
"unexpected positions\nstart_pos: {:?}\nend_pos: {:?}",
start_pos,
end_pos
);
return Err(LspError::internal_error());
}
} }
builder.build(None) Ok(builder.build(None))
} }
fn get_token_type_from_classification(ts_classification: u32) -> u32 { fn get_token_type_from_classification(ts_classification: u32) -> u32 {

View file

@ -1066,10 +1066,10 @@ fn lsp_hover_dependency() {
); );
} }
// Regression test for #12753 // This tests for a regression covered by denoland/deno#12753 where the lsp was
// unable to resolve dependencies when there was an invalid syntax in the module
#[test] #[test]
#[ignore] fn lsp_hover_deps_preserved_when_invalid_parse() {
fn lsp_hover_keep_type_info_after_invalid_syntax_change() {
let mut client = init("initialize_params.json"); let mut client = init("initialize_params.json");
did_open( did_open(
&mut client, &mut client,