mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
feat(lsp): diagnostics for deno types and triple-slash refs (#10699)
Fixes #9823
This commit is contained in:
parent
7b6bba5d3a
commit
6bbefdff39
6 changed files with 335 additions and 75 deletions
|
@ -140,6 +140,7 @@ pub struct Dependency {
|
||||||
pub maybe_code: Option<ResolvedDependency>,
|
pub maybe_code: Option<ResolvedDependency>,
|
||||||
pub maybe_code_specifier_range: Option<Range>,
|
pub maybe_code_specifier_range: Option<Range>,
|
||||||
pub maybe_type: Option<ResolvedDependency>,
|
pub maybe_type: Option<ResolvedDependency>,
|
||||||
|
pub maybe_type_specifier_range: Option<Range>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -259,13 +260,24 @@ pub fn analyze_dependencies(
|
||||||
|
|
||||||
// Parse leading comments for supported triple slash references.
|
// Parse leading comments for supported triple slash references.
|
||||||
for comment in parsed_module.get_leading_comments().iter() {
|
for comment in parsed_module.get_leading_comments().iter() {
|
||||||
if let Some(ts_reference) = parse_ts_reference(&comment.text) {
|
if let Some((ts_reference, span)) = parse_ts_reference(&comment) {
|
||||||
|
let loc = parsed_module.source_map.lookup_char_pos(span.lo);
|
||||||
match ts_reference {
|
match ts_reference {
|
||||||
TypeScriptReference::Path(import) => {
|
TypeScriptReference::Path(import) => {
|
||||||
let dep = dependencies.entry(import.clone()).or_default();
|
let dep = dependencies.entry(import.clone()).or_default();
|
||||||
let resolved_import =
|
let resolved_import =
|
||||||
resolve_import(&import, specifier, maybe_import_map);
|
resolve_import(&import, specifier, maybe_import_map);
|
||||||
dep.maybe_code = Some(resolved_import);
|
dep.maybe_code = Some(resolved_import);
|
||||||
|
dep.maybe_code_specifier_range = Some(Range {
|
||||||
|
start: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: loc.col_display as u32,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: (loc.col_display + import.chars().count() + 2) as u32,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
TypeScriptReference::Types(import) => {
|
TypeScriptReference::Types(import) => {
|
||||||
let resolved_import =
|
let resolved_import =
|
||||||
|
@ -273,11 +285,20 @@ pub fn analyze_dependencies(
|
||||||
if media_type == &MediaType::JavaScript
|
if media_type == &MediaType::JavaScript
|
||||||
|| media_type == &MediaType::Jsx
|
|| media_type == &MediaType::Jsx
|
||||||
{
|
{
|
||||||
maybe_type = Some(resolved_import)
|
maybe_type = Some(resolved_import.clone());
|
||||||
} else {
|
|
||||||
let dep = dependencies.entry(import).or_default();
|
|
||||||
dep.maybe_type = Some(resolved_import);
|
|
||||||
}
|
}
|
||||||
|
let dep = dependencies.entry(import.clone()).or_default();
|
||||||
|
dep.maybe_type = Some(resolved_import);
|
||||||
|
dep.maybe_type_specifier_range = Some(Range {
|
||||||
|
start: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: loc.col_display as u32,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: (loc.col_display + import.chars().count() + 2) as u32,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,7 +315,13 @@ pub fn analyze_dependencies(
|
||||||
let maybe_resolved_type_dependency =
|
let maybe_resolved_type_dependency =
|
||||||
// Check for `@deno-types` pragmas that affect the import
|
// Check for `@deno-types` pragmas that affect the import
|
||||||
if let Some(comment) = desc.leading_comments.last() {
|
if let Some(comment) = desc.leading_comments.last() {
|
||||||
parse_deno_types(&comment.text).as_ref().map(|deno_types| resolve_import(deno_types, specifier, maybe_import_map))
|
parse_deno_types(&comment).as_ref().map(|(deno_types, span)| {
|
||||||
|
(
|
||||||
|
resolve_import(deno_types, specifier, maybe_import_map),
|
||||||
|
deno_types.clone(),
|
||||||
|
parsed_module.source_map.lookup_char_pos(span.lo)
|
||||||
|
)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -304,6 +331,17 @@ pub fn analyze_dependencies(
|
||||||
match desc.kind {
|
match desc.kind {
|
||||||
swc_ecmascript::dep_graph::DependencyKind::ExportType
|
swc_ecmascript::dep_graph::DependencyKind::ExportType
|
||||||
| swc_ecmascript::dep_graph::DependencyKind::ImportType => {
|
| swc_ecmascript::dep_graph::DependencyKind::ImportType => {
|
||||||
|
dep.maybe_type_specifier_range = Some(Range {
|
||||||
|
start: Position {
|
||||||
|
line: (desc.specifier_line - 1) as u32,
|
||||||
|
character: desc.specifier_col as u32,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: (desc.specifier_line - 1) as u32,
|
||||||
|
character: (desc.specifier_col + desc.specifier.chars().count() + 2)
|
||||||
|
as u32,
|
||||||
|
},
|
||||||
|
});
|
||||||
dep.maybe_type = Some(resolved_import)
|
dep.maybe_type = Some(resolved_import)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -321,8 +359,22 @@ pub fn analyze_dependencies(
|
||||||
dep.maybe_code = Some(resolved_import);
|
dep.maybe_code = Some(resolved_import);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if maybe_resolved_type_dependency.is_some() && dep.maybe_type.is_none() {
|
if dep.maybe_type.is_none() {
|
||||||
dep.maybe_type = maybe_resolved_type_dependency;
|
if let Some((resolved_dependency, specifier, loc)) =
|
||||||
|
maybe_resolved_type_dependency
|
||||||
|
{
|
||||||
|
dep.maybe_type_specifier_range = Some(Range {
|
||||||
|
start: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: (loc.col_display + 1) as u32,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: (loc.line - 1) as u32,
|
||||||
|
character: (loc.col_display + 1 + specifier.chars().count()) as u32,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dep.maybe_type = Some(resolved_dependency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,6 +775,16 @@ mod tests {
|
||||||
character: 58,
|
character: 58,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
maybe_type_specifier_range: Some(Range {
|
||||||
|
start: Position {
|
||||||
|
line: 7,
|
||||||
|
character: 20,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: 7,
|
||||||
|
character: 62,
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -743,6 +805,7 @@ mod tests {
|
||||||
character: 50,
|
character: 50,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
maybe_type_specifier_range: None,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use super::analysis;
|
use super::analysis;
|
||||||
|
use super::documents::DocumentCache;
|
||||||
use super::language_server;
|
use super::language_server;
|
||||||
|
use super::sources::Sources;
|
||||||
use super::tsc;
|
use super::tsc;
|
||||||
|
|
||||||
use crate::diagnostics;
|
use crate::diagnostics;
|
||||||
use crate::media_type::MediaType;
|
use crate::media_type::MediaType;
|
||||||
use crate::tokio_util::create_basic_runtime;
|
use crate::tokio_util::create_basic_runtime;
|
||||||
|
|
||||||
|
use analysis::ResolvedDependency;
|
||||||
use deno_core::error::anyhow;
|
use deno_core::error::anyhow;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::resolve_url;
|
use deno_core::resolve_url;
|
||||||
|
@ -392,37 +395,17 @@ async fn generate_ts_diagnostics(
|
||||||
Ok(diagnostics_vec)
|
Ok(diagnostics_vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate diagnostics for dependencies of a module, attempting to resolve
|
fn diagnose_dependency(
|
||||||
/// dependencies on the local file system or in the DENO_DIR cache.
|
diagnostics: &mut Vec<lsp::Diagnostic>,
|
||||||
async fn generate_deps_diagnostics(
|
documents: &DocumentCache,
|
||||||
snapshot: &language_server::StateSnapshot,
|
sources: &Sources,
|
||||||
collection: Arc<Mutex<DiagnosticCollection>>,
|
maybe_dependency: &Option<ResolvedDependency>,
|
||||||
) -> Result<DiagnosticVec, AnyError> {
|
maybe_range: &Option<lsp::Range>,
|
||||||
let config = snapshot.config.clone();
|
) {
|
||||||
let documents = snapshot.documents.clone();
|
if let (Some(dep), Some(range)) = (maybe_dependency, *maybe_range) {
|
||||||
let sources = snapshot.sources.clone();
|
match dep {
|
||||||
tokio::task::spawn(async move {
|
analysis::ResolvedDependency::Err(err) => {
|
||||||
let mut diagnostics_vec = Vec::new();
|
diagnostics.push(lsp::Diagnostic {
|
||||||
|
|
||||||
for specifier in documents.open_specifiers() {
|
|
||||||
if !config.specifier_enabled(specifier) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let version = documents.version(specifier);
|
|
||||||
let current_version = collection
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.get_version(specifier, &DiagnosticSource::Deno);
|
|
||||||
if version != current_version {
|
|
||||||
let mut diagnostics = Vec::new();
|
|
||||||
if let Some(dependencies) = documents.dependencies(specifier) {
|
|
||||||
for (_, dependency) in dependencies {
|
|
||||||
// TODO(@kitsonk) add diagnostics for maybe_type dependencies
|
|
||||||
if let (Some(code), Some(range)) =
|
|
||||||
(dependency.maybe_code, dependency.maybe_code_specifier_range)
|
|
||||||
{
|
|
||||||
match code {
|
|
||||||
analysis::ResolvedDependency::Err(err) => diagnostics.push(lsp::Diagnostic {
|
|
||||||
range,
|
range,
|
||||||
severity: Some(lsp::DiagnosticSeverity::Error),
|
severity: Some(lsp::DiagnosticSeverity::Error),
|
||||||
code: Some(err.as_code()),
|
code: Some(err.as_code()),
|
||||||
|
@ -432,9 +415,12 @@ async fn generate_deps_diagnostics(
|
||||||
related_information: None,
|
related_information: None,
|
||||||
tags: None,
|
tags: None,
|
||||||
data: None,
|
data: None,
|
||||||
}),
|
})
|
||||||
|
}
|
||||||
analysis::ResolvedDependency::Resolved(specifier) => {
|
analysis::ResolvedDependency::Resolved(specifier) => {
|
||||||
if !(documents.contains_key(&specifier) || sources.contains_key(&specifier)) {
|
if !(documents.contains_key(&specifier)
|
||||||
|
|| sources.contains_key(&specifier))
|
||||||
|
{
|
||||||
let (code, message) = match specifier.scheme() {
|
let (code, message) = match specifier.scheme() {
|
||||||
"file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)),
|
"file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)),
|
||||||
"data" => (Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string()),
|
"data" => (Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string()),
|
||||||
|
@ -462,11 +448,52 @@ async fn generate_deps_diagnostics(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate diagnostics for dependencies of a module, attempting to resolve
|
||||||
|
/// dependencies on the local file system or in the DENO_DIR cache.
|
||||||
|
async fn generate_deps_diagnostics(
|
||||||
|
snapshot: &language_server::StateSnapshot,
|
||||||
|
collection: Arc<Mutex<DiagnosticCollection>>,
|
||||||
|
) -> Result<DiagnosticVec, AnyError> {
|
||||||
|
let config = snapshot.config.clone();
|
||||||
|
let documents = snapshot.documents.clone();
|
||||||
|
let sources = snapshot.sources.clone();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
let mut diagnostics_vec = Vec::new();
|
||||||
|
|
||||||
|
for specifier in documents.open_specifiers() {
|
||||||
|
if !config.specifier_enabled(specifier) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let version = documents.version(specifier);
|
||||||
|
let current_version = collection
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.get_version(specifier, &DiagnosticSource::Deno);
|
||||||
|
if version != current_version {
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
if let Some(dependencies) = documents.dependencies(specifier) {
|
||||||
|
for (_, dependency) in dependencies {
|
||||||
|
diagnose_dependency(
|
||||||
|
&mut diagnostics,
|
||||||
|
&documents,
|
||||||
|
&sources,
|
||||||
|
&dependency.maybe_code,
|
||||||
|
&dependency.maybe_code_specifier_range,
|
||||||
|
);
|
||||||
|
diagnose_dependency(
|
||||||
|
&mut diagnostics,
|
||||||
|
&documents,
|
||||||
|
&sources,
|
||||||
|
&dependency.maybe_type,
|
||||||
|
&dependency.maybe_type_specifier_range,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
diagnostics_vec.push((specifier.clone(), version, diagnostics));
|
diagnostics_vec.push((specifier.clone(), version, diagnostics));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,9 @@ use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use swc_common::comments::Comment;
|
||||||
|
use swc_common::BytePos;
|
||||||
|
use swc_common::Span;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
/// Matched the `@deno-types` pragma.
|
/// Matched the `@deno-types` pragma.
|
||||||
|
@ -188,35 +191,48 @@ pub enum TypeScriptReference {
|
||||||
Types(String),
|
Types(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_to_span(comment: &Comment, m: ®ex::Match) -> Span {
|
||||||
|
Span {
|
||||||
|
lo: comment.span.lo + BytePos((m.start() + 1) as u32),
|
||||||
|
hi: comment.span.lo + BytePos((m.end() + 1) as u32),
|
||||||
|
ctxt: comment.span.ctxt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine if a comment contains a triple slash reference and optionally
|
/// Determine if a comment contains a triple slash reference and optionally
|
||||||
/// return its kind and value.
|
/// return its kind and value.
|
||||||
pub fn parse_ts_reference(comment: &str) -> Option<TypeScriptReference> {
|
pub fn parse_ts_reference(
|
||||||
if !TRIPLE_SLASH_REFERENCE_RE.is_match(comment) {
|
comment: &Comment,
|
||||||
|
) -> Option<(TypeScriptReference, Span)> {
|
||||||
|
if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) {
|
||||||
None
|
None
|
||||||
} else if let Some(captures) = PATH_REFERENCE_RE.captures(comment) {
|
} else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) {
|
||||||
Some(TypeScriptReference::Path(
|
let m = captures.get(1).unwrap();
|
||||||
captures.get(1).unwrap().as_str().to_string(),
|
Some((
|
||||||
|
TypeScriptReference::Path(m.as_str().to_string()),
|
||||||
|
match_to_span(comment, &m),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
TYPES_REFERENCE_RE.captures(comment).map(|captures| {
|
TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| {
|
||||||
TypeScriptReference::Types(captures.get(1).unwrap().as_str().to_string())
|
let m = captures.get(1).unwrap();
|
||||||
|
(
|
||||||
|
TypeScriptReference::Types(m.as_str().to_string()),
|
||||||
|
match_to_span(comment, &m),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if a comment contains a `@deno-types` pragma and optionally return
|
/// Determine if a comment contains a `@deno-types` pragma and optionally return
|
||||||
/// its value.
|
/// its value.
|
||||||
pub fn parse_deno_types(comment: &str) -> Option<String> {
|
pub fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> {
|
||||||
if let Some(captures) = DENO_TYPES_RE.captures(comment) {
|
let captures = DENO_TYPES_RE.captures(&comment.text)?;
|
||||||
if let Some(m) = captures.get(1) {
|
if let Some(m) = captures.get(1) {
|
||||||
Some(m.as_str().to_string())
|
Some((m.as_str().to_string(), match_to_span(comment, &m)))
|
||||||
} else if let Some(m) = captures.get(2) {
|
} else if let Some(m) = captures.get(2) {
|
||||||
Some(m.as_str().to_string())
|
Some((m.as_str().to_string(), match_to_span(comment, &m)))
|
||||||
} else {
|
} else {
|
||||||
panic!("unreachable");
|
unreachable!();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +343,7 @@ impl Module {
|
||||||
|
|
||||||
// parse out any triple slash references
|
// parse out any triple slash references
|
||||||
for comment in parsed_module.get_leading_comments().iter() {
|
for comment in parsed_module.get_leading_comments().iter() {
|
||||||
if let Some(ts_reference) = parse_ts_reference(&comment.text) {
|
if let Some((ts_reference, _)) = parse_ts_reference(&comment) {
|
||||||
let location = parsed_module.get_location(&comment.span);
|
let location = parsed_module.get_location(&comment.span);
|
||||||
match ts_reference {
|
match ts_reference {
|
||||||
TypeScriptReference::Path(import) => {
|
TypeScriptReference::Path(import) => {
|
||||||
|
@ -392,7 +408,7 @@ impl Module {
|
||||||
// Parse out any `@deno-types` pragmas and modify dependency
|
// Parse out any `@deno-types` pragmas and modify dependency
|
||||||
let maybe_type = if !desc.leading_comments.is_empty() {
|
let maybe_type = if !desc.leading_comments.is_empty() {
|
||||||
let comment = desc.leading_comments.last().unwrap();
|
let comment = desc.leading_comments.last().unwrap();
|
||||||
if let Some(deno_types) = parse_deno_types(&comment.text).as_ref() {
|
if let Some((deno_types, _)) = parse_deno_types(&comment).as_ref() {
|
||||||
Some(self.resolve_import(deno_types, Some(location.clone()))?)
|
Some(self.resolve_import(deno_types, Some(location.clone()))?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1398,6 +1398,11 @@ fn lsp_code_actions_deno_cache() {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": true }))
|
||||||
|
.unwrap();
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
@ -1851,6 +1856,46 @@ fn lsp_diagnostics_warn() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_diagnostics_deno_types() {
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
load_fixture("did_open_params_deno_types.json"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": true }))
|
||||||
|
.unwrap();
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"textDocument/documentSymbol",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_res.is_some());
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
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_eq!(params.diagnostics.len(), 5);
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PerformanceAverage {
|
pub struct PerformanceAverage {
|
||||||
|
|
101
cli/tests/lsp/diagnostics_deno_types.json
Normal file
101
cli/tests/lsp/diagnostics_deno_types.json
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"uri": "file:///a/file.ts",
|
||||||
|
"diagnostics": [
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 21
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": "no-cache",
|
||||||
|
"source": "deno",
|
||||||
|
"message": "Uncached or missing remote URL: \"https://example.com/a/b.d.ts\".",
|
||||||
|
"data": {
|
||||||
|
"specifier": "https://example.com/a/b.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 7,
|
||||||
|
"character": 19
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 7,
|
||||||
|
"character": 47
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": "no-cache",
|
||||||
|
"source": "deno",
|
||||||
|
"message": "Uncached or missing remote URL: \"https://example.com/a/e.js\".",
|
||||||
|
"data": {
|
||||||
|
"specifier": "https://example.com/a/e.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 6,
|
||||||
|
"character": 16
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 6,
|
||||||
|
"character": 44
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": "no-cache",
|
||||||
|
"source": "deno",
|
||||||
|
"message": "Uncached or missing remote URL: \"https://example.com/a/e.d.ts\".",
|
||||||
|
"data": {
|
||||||
|
"specifier": "https://example.com/a/e.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 19
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 47
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": "no-cache",
|
||||||
|
"source": "deno",
|
||||||
|
"message": "Uncached or missing remote URL: \"https://example.com/a/d.js\".",
|
||||||
|
"data": {
|
||||||
|
"specifier": "https://example.com/a/d.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 3,
|
||||||
|
"character": 15
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 3,
|
||||||
|
"character": 43
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"severity": 1,
|
||||||
|
"code": "no-cache",
|
||||||
|
"source": "deno",
|
||||||
|
"message": "Uncached or missing remote URL: \"https://example.com/a/d.d.ts\".",
|
||||||
|
"data": {
|
||||||
|
"specifier": "https://example.com/a/d.d.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 1
|
||||||
|
}
|
8
cli/tests/lsp/did_open_params_deno_types.json
Normal file
8
cli/tests/lsp/did_open_params_deno_types.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "/// <reference types=\"https://example.com/a/b.d.ts\" />\n/// <reference path=\"https://example.com/a/c.ts\"\n\n// @deno-types=https://example.com/a/d.d.ts\nimport * as d from \"https://example.com/a/d.js\";\n\n// @deno-types=\"https://example.com/a/e.d.ts\"\nimport * as e from \"https://example.com/a/e.js\";\n\nconsole.log(d, e);\n"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue