mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Fixes #10775
This commit is contained in:
parent
5a14827f9f
commit
844a1317ec
4 changed files with 214 additions and 3 deletions
|
@ -120,8 +120,11 @@ impl DiagnosticsServer {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn invalidate(&self, specifier: &ModuleSpecifier) {
|
pub(crate) async fn invalidate(&self, specifiers: Vec<ModuleSpecifier>) {
|
||||||
self.collection.lock().await.versions.remove(specifier);
|
let mut collection = self.collection.lock().await;
|
||||||
|
for specifier in specifiers {
|
||||||
|
collection.versions.remove(&specifier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start(
|
pub(crate) fn start(
|
||||||
|
|
|
@ -12,6 +12,7 @@ use deno_core::error::Context;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use lspower::lsp::TextDocumentContentChangeEvent;
|
use lspower::lsp::TextDocumentContentChangeEvent;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -138,10 +139,42 @@ impl DocumentData {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct DocumentCache {
|
pub struct DocumentCache {
|
||||||
|
dependents_graph: HashMap<ModuleSpecifier, HashSet<ModuleSpecifier>>,
|
||||||
docs: HashMap<ModuleSpecifier, DocumentData>,
|
docs: HashMap<ModuleSpecifier, DocumentData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentCache {
|
impl DocumentCache {
|
||||||
|
/// Calculate a graph of dependents and set it on the structure.
|
||||||
|
fn calculate_dependents(&mut self) {
|
||||||
|
let mut dependents_graph: HashMap<
|
||||||
|
ModuleSpecifier,
|
||||||
|
HashSet<ModuleSpecifier>,
|
||||||
|
> = HashMap::new();
|
||||||
|
for (specifier, data) in &self.docs {
|
||||||
|
if let Some(dependencies) = &data.dependencies {
|
||||||
|
for dependency in dependencies.values() {
|
||||||
|
if let Some(analysis::ResolvedDependency::Resolved(dep_specifier)) =
|
||||||
|
&dependency.maybe_code
|
||||||
|
{
|
||||||
|
dependents_graph
|
||||||
|
.entry(dep_specifier.clone())
|
||||||
|
.or_default()
|
||||||
|
.insert(specifier.clone());
|
||||||
|
}
|
||||||
|
if let Some(analysis::ResolvedDependency::Resolved(dep_specifier)) =
|
||||||
|
&dependency.maybe_type
|
||||||
|
{
|
||||||
|
dependents_graph
|
||||||
|
.entry(dep_specifier.clone())
|
||||||
|
.or_default()
|
||||||
|
.insert(specifier.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dependents_graph = dependents_graph;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn change(
|
pub fn change(
|
||||||
&mut self,
|
&mut self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -166,6 +199,7 @@ impl DocumentCache {
|
||||||
|
|
||||||
pub fn close(&mut self, specifier: &ModuleSpecifier) {
|
pub fn close(&mut self, specifier: &ModuleSpecifier) {
|
||||||
self.docs.remove(specifier);
|
self.docs.remove(specifier);
|
||||||
|
self.calculate_dependents();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_key(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn contains_key(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
|
@ -183,6 +217,17 @@ impl DocumentCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For a given specifier, get all open documents which directly or indirectly
|
||||||
|
// depend upon the specifier.
|
||||||
|
pub fn dependents(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> Vec<ModuleSpecifier> {
|
||||||
|
let mut dependents = HashSet::new();
|
||||||
|
self.recurse_dependents(specifier, &mut dependents);
|
||||||
|
dependents.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dependencies(
|
pub fn dependencies(
|
||||||
&self,
|
&self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -260,6 +305,21 @@ impl DocumentCache {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recurse_dependents(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
dependents: &mut HashSet<ModuleSpecifier>,
|
||||||
|
) {
|
||||||
|
if let Some(deps) = self.dependents_graph.get(specifier) {
|
||||||
|
for dep in deps {
|
||||||
|
if !dependents.contains(dep) {
|
||||||
|
dependents.insert(dep.clone());
|
||||||
|
self.recurse_dependents(dep, dependents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_dependencies(
|
pub fn set_dependencies(
|
||||||
&mut self,
|
&mut self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -267,6 +327,7 @@ impl DocumentCache {
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
if let Some(doc) = self.docs.get_mut(specifier) {
|
if let Some(doc) = self.docs.get_mut(specifier) {
|
||||||
doc.dependencies = maybe_dependencies;
|
doc.dependencies = maybe_dependencies;
|
||||||
|
self.calculate_dependents();
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(custom_error(
|
Err(custom_error(
|
||||||
|
|
|
@ -627,6 +627,10 @@ impl Inner {
|
||||||
|
|
||||||
if self.documents.is_diagnosable(&specifier) {
|
if self.documents.is_diagnosable(&specifier) {
|
||||||
self.analyze_dependencies(&specifier, ¶ms.text_document.text);
|
self.analyze_dependencies(&specifier, ¶ms.text_document.text);
|
||||||
|
self
|
||||||
|
.diagnostics_server
|
||||||
|
.invalidate(self.documents.dependents(&specifier))
|
||||||
|
.await;
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
@ -645,6 +649,10 @@ impl Inner {
|
||||||
Ok(Some(source)) => {
|
Ok(Some(source)) => {
|
||||||
if self.documents.is_diagnosable(&specifier) {
|
if self.documents.is_diagnosable(&specifier) {
|
||||||
self.analyze_dependencies(&specifier, &source);
|
self.analyze_dependencies(&specifier, &source);
|
||||||
|
self
|
||||||
|
.diagnostics_server
|
||||||
|
.invalidate(self.documents.dependents(&specifier))
|
||||||
|
.await;
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
@ -2511,7 +2519,7 @@ impl Inner {
|
||||||
if let Some(source) = self.documents.content(&referrer).unwrap() {
|
if let Some(source) = self.documents.content(&referrer).unwrap() {
|
||||||
self.analyze_dependencies(&referrer, &source);
|
self.analyze_dependencies(&referrer, &source);
|
||||||
}
|
}
|
||||||
self.diagnostics_server.invalidate(&referrer).await;
|
self.diagnostics_server.invalidate(vec![referrer]).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.diagnostics_server.update().map_err(|err| {
|
self.diagnostics_server.update().map_err(|err| {
|
||||||
|
|
|
@ -1994,6 +1994,145 @@ fn lsp_diagnostics_deno_types() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[test]
|
||||||
|
fn lsp_diagnostics_refresh_dependents() {
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
did_open(
|
||||||
|
&mut client,
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file_00.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "export const a = \"a\";\n",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
did_open(
|
||||||
|
&mut client,
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file_01.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "export * from \"./file_00.ts\";\n",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file_02.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "import { a, b } from \"./file_01.ts\";\n\nconsole.log(a, b);\n"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": false }))
|
||||||
|
.unwrap();
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, maybe_params) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
assert_eq!(
|
||||||
|
maybe_params,
|
||||||
|
Some(json!({
|
||||||
|
"uri": "file:///a/file_02.ts",
|
||||||
|
"diagnostics": [
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 12
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": 2305,
|
||||||
|
"source": "deno-ts",
|
||||||
|
"message": "Module '\"./file_01.ts\"' has no exported member 'b'."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 1
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didChange",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file_00.ts",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"contentChanges": [
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"character": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text": "export const b = \"b\";\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, maybe_params) = client
|
||||||
|
.read_notification::<lsp::PublishDiagnosticsParams>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
assert!(maybe_params.is_some());
|
||||||
|
let params = maybe_params.unwrap();
|
||||||
|
assert!(params.diagnostics.is_empty());
|
||||||
|
let (method, maybe_params) = client
|
||||||
|
.read_notification::<lsp::PublishDiagnosticsParams>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
assert!(maybe_params.is_some());
|
||||||
|
let params = maybe_params.unwrap();
|
||||||
|
assert!(params.diagnostics.is_empty());
|
||||||
|
let (method, maybe_params) = client
|
||||||
|
.read_notification::<lsp::PublishDiagnosticsParams>()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
assert!(maybe_params.is_some());
|
||||||
|
let params = maybe_params.unwrap();
|
||||||
|
assert!(params.diagnostics.is_empty());
|
||||||
|
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PerformanceAverage {
|
pub struct PerformanceAverage {
|
||||||
|
|
Loading…
Add table
Reference in a new issue