2021-01-11 12:13:41 -05:00
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2020-12-07 05:46:39 -05:00
2021-05-11 10:43:00 -04:00
use super ::analysis ;
2021-05-24 22:34:01 -04:00
use super ::documents ::DocumentCache ;
2021-03-09 21:41:35 -05:00
use super ::language_server ;
2021-05-24 22:34:01 -04:00
use super ::sources ::Sources ;
2020-12-07 05:46:39 -05:00
use super ::tsc ;
use crate ::diagnostics ;
use crate ::media_type ::MediaType ;
2021-03-09 21:41:35 -05:00
use crate ::tokio_util ::create_basic_runtime ;
2020-12-07 05:46:39 -05:00
2021-05-24 22:34:01 -04:00
use analysis ::ResolvedDependency ;
2021-03-09 21:41:35 -05:00
use deno_core ::error ::anyhow ;
2020-12-07 05:46:39 -05:00
use deno_core ::error ::AnyError ;
2021-05-11 10:43:00 -04:00
use deno_core ::resolve_url ;
2021-05-19 08:28:23 -04:00
use deno_core ::serde_json ::json ;
2021-01-22 05:03:16 -05:00
use deno_core ::ModuleSpecifier ;
2021-03-26 12:34:25 -04:00
use log ::error ;
2021-01-29 14:34:33 -05:00
use lspower ::lsp ;
2020-12-07 05:46:39 -05:00
use std ::collections ::HashMap ;
use std ::collections ::HashSet ;
use std ::mem ;
2021-03-09 21:41:35 -05:00
use std ::sync ::Arc ;
use std ::thread ;
use tokio ::sync ::mpsc ;
2021-05-11 10:43:00 -04:00
use tokio ::sync ::Mutex ;
2021-03-18 16:26:41 -04:00
use tokio ::time ::sleep ;
use tokio ::time ::Duration ;
use tokio ::time ::Instant ;
2020-12-07 05:46:39 -05:00
2021-05-11 10:43:00 -04:00
pub type DiagnosticRecord =
( ModuleSpecifier , Option < i32 > , Vec < lsp ::Diagnostic > ) ;
pub type DiagnosticVec = Vec < DiagnosticRecord > ;
type TsDiagnosticsMap = HashMap < String , Vec < diagnostics ::Diagnostic > > ;
#[ derive(Debug, Hash, Clone, PartialEq, Eq) ]
pub ( crate ) enum DiagnosticSource {
2020-12-24 05:53:03 -05:00
Deno ,
2021-05-11 10:43:00 -04:00
DenoLint ,
2020-12-07 05:46:39 -05:00
TypeScript ,
}
2021-05-11 10:43:00 -04:00
#[ derive(Debug, Default) ]
struct DiagnosticCollection {
map : HashMap < ( ModuleSpecifier , DiagnosticSource ) , Vec < lsp ::Diagnostic > > ,
versions : HashMap < ModuleSpecifier , HashMap < DiagnosticSource , i32 > > ,
changes : HashSet < ModuleSpecifier > ,
2021-03-09 21:41:35 -05:00
}
2021-05-11 10:43:00 -04:00
impl DiagnosticCollection {
pub fn get (
& self ,
specifier : & ModuleSpecifier ,
source : DiagnosticSource ,
) -> impl Iterator < Item = & lsp ::Diagnostic > {
self
. map
. get ( & ( specifier . clone ( ) , source ) )
. into_iter ( )
. flatten ( )
2021-03-09 21:41:35 -05:00
}
2021-05-11 10:43:00 -04:00
pub fn get_version (
& self ,
specifier : & ModuleSpecifier ,
source : & DiagnosticSource ,
) -> Option < i32 > {
let source_version = self . versions . get ( specifier ) ? ;
source_version . get ( source ) . cloned ( )
}
2021-03-09 21:41:35 -05:00
2021-05-11 10:43:00 -04:00
pub fn set ( & mut self , source : DiagnosticSource , record : DiagnosticRecord ) {
let ( specifier , maybe_version , diagnostics ) = record ;
self
. map
. insert ( ( specifier . clone ( ) , source . clone ( ) ) , diagnostics ) ;
if let Some ( version ) = maybe_version {
let source_version = self . versions . entry ( specifier . clone ( ) ) . or_default ( ) ;
source_version . insert ( source , version ) ;
2021-05-09 21:16:04 -04:00
}
2021-05-11 10:43:00 -04:00
self . changes . insert ( specifier ) ;
}
2021-03-09 21:41:35 -05:00
2021-05-11 10:43:00 -04:00
pub fn take_changes ( & mut self ) -> Option < HashSet < ModuleSpecifier > > {
if self . changes . is_empty ( ) {
None
} else {
Some ( mem ::take ( & mut self . changes ) )
2021-05-09 21:16:04 -04:00
}
2021-05-11 10:43:00 -04:00
}
2021-03-09 21:41:35 -05:00
}
#[ derive(Debug) ]
2021-05-11 10:43:00 -04:00
pub ( crate ) struct DiagnosticsServer {
channel : Option < mpsc ::UnboundedSender < ( ) > > ,
collection : Arc < Mutex < DiagnosticCollection > > ,
}
2021-03-09 21:41:35 -05:00
impl DiagnosticsServer {
pub ( crate ) fn new ( ) -> Self {
2021-05-11 10:43:00 -04:00
let collection = Arc ::new ( Mutex ::new ( DiagnosticCollection ::default ( ) ) ) ;
Self {
channel : None ,
collection ,
}
}
pub ( crate ) async fn get (
& self ,
specifier : & ModuleSpecifier ,
source : DiagnosticSource ,
) -> Vec < lsp ::Diagnostic > {
self
. collection
. lock ( )
. await
. get ( specifier , source )
. cloned ( )
. collect ( )
}
pub ( crate ) async fn invalidate ( & self , specifier : & ModuleSpecifier ) {
self . collection . lock ( ) . await . versions . remove ( specifier ) ;
2021-03-09 21:41:35 -05:00
}
pub ( crate ) fn start (
& mut self ,
2021-05-11 10:43:00 -04:00
language_server : Arc < Mutex < language_server ::Inner > > ,
client : lspower ::Client ,
2021-03-09 21:41:35 -05:00
ts_server : Arc < tsc ::TsServer > ,
) {
2021-05-11 10:43:00 -04:00
let ( tx , mut rx ) = mpsc ::unbounded_channel ::< ( ) > ( ) ;
self . channel = Some ( tx ) ;
let collection = self . collection . clone ( ) ;
2021-03-09 21:41:35 -05:00
let _join_handle = thread ::spawn ( move | | {
let runtime = create_basic_runtime ( ) ;
runtime . block_on ( async {
2021-03-18 16:26:41 -04:00
// Debounce timer delay. 150ms between keystrokes is about 45 WPM, so we
// want something that is longer than that, but not too long to
// introduce detectable UI delay; 200ms is a decent compromise.
const DELAY : Duration = Duration ::from_millis ( 200 ) ;
// If the debounce timer isn't active, it will be set to expire "never",
// which is actually just 1 year in the future.
const NEVER : Duration = Duration ::from_secs ( 365 * 24 * 60 * 60 ) ;
2021-03-09 21:41:35 -05:00
2021-03-18 16:26:41 -04:00
// A flag that is set whenever something has changed that requires the
// diagnostics collection to be updated.
let mut dirty = false ;
2021-03-09 21:41:35 -05:00
2021-03-18 16:26:41 -04:00
let debounce_timer = sleep ( NEVER ) ;
tokio ::pin! ( debounce_timer ) ;
2021-03-09 21:41:35 -05:00
2021-03-18 16:26:41 -04:00
loop {
2021-03-09 21:41:35 -05:00
// "race" the next message off the rx queue or the debounce timer.
2021-03-18 16:26:41 -04:00
// The debounce timer gets reset every time a message comes off the
// queue. When the debounce timer expires, a snapshot of the most
// up-to-date state is used to produce diagnostics.
2021-03-09 21:41:35 -05:00
tokio ::select! {
2021-03-18 16:26:41 -04:00
maybe_request = rx . recv ( ) = > {
match maybe_request {
2021-05-11 10:43:00 -04:00
// channel has closed
None = > break ,
Some ( _ ) = > {
2021-03-18 16:26:41 -04:00
dirty = true ;
debounce_timer . as_mut ( ) . reset ( Instant ::now ( ) + DELAY ) ;
}
2021-03-09 21:41:35 -05:00
}
}
2021-03-18 16:26:41 -04:00
_ = debounce_timer . as_mut ( ) , if dirty = > {
dirty = false ;
debounce_timer . as_mut ( ) . reset ( Instant ::now ( ) + NEVER ) ;
2021-05-20 05:56:48 -04:00
let snapshot = language_server . lock ( ) . await . snapshot ( ) . unwrap ( ) ;
2021-03-18 16:26:41 -04:00
update_diagnostics (
& client ,
2021-05-07 07:05:32 -04:00
collection . clone ( ) ,
2021-03-18 16:26:41 -04:00
& snapshot ,
& ts_server
) . await ;
2021-03-09 21:41:35 -05:00
}
}
}
} )
} ) ;
}
2021-05-11 10:43:00 -04:00
pub ( crate ) fn update ( & self ) -> Result < ( ) , AnyError > {
if let Some ( tx ) = & self . channel {
tx . send ( ( ) ) . map_err ( | err | err . into ( ) )
2021-05-07 07:05:32 -04:00
} else {
2021-05-11 10:43:00 -04:00
Err ( anyhow! ( " diagnostics server not started " ) )
2021-05-07 07:05:32 -04:00
}
2020-12-07 05:46:39 -05:00
}
2020-12-21 08:44:26 -05:00
}
2021-01-29 14:34:33 -05:00
impl < ' a > From < & ' a diagnostics ::DiagnosticCategory > for lsp ::DiagnosticSeverity {
2020-12-21 08:44:26 -05:00
fn from ( category : & ' a diagnostics ::DiagnosticCategory ) -> Self {
match category {
2021-01-29 14:34:33 -05:00
diagnostics ::DiagnosticCategory ::Error = > lsp ::DiagnosticSeverity ::Error ,
2020-12-21 08:44:26 -05:00
diagnostics ::DiagnosticCategory ::Warning = > {
2021-01-29 14:34:33 -05:00
lsp ::DiagnosticSeverity ::Warning
2020-12-21 08:44:26 -05:00
}
diagnostics ::DiagnosticCategory ::Suggestion = > {
2021-01-29 14:34:33 -05:00
lsp ::DiagnosticSeverity ::Hint
2020-12-21 08:44:26 -05:00
}
diagnostics ::DiagnosticCategory ::Message = > {
2021-01-29 14:34:33 -05:00
lsp ::DiagnosticSeverity ::Information
2020-12-21 08:44:26 -05:00
}
}
}
}
2021-01-29 14:34:33 -05:00
impl < ' a > From < & ' a diagnostics ::Position > for lsp ::Position {
2020-12-21 08:44:26 -05:00
fn from ( pos : & ' a diagnostics ::Position ) -> Self {
Self {
line : pos . line as u32 ,
character : pos . character as u32 ,
}
2020-12-07 05:46:39 -05:00
}
2020-12-21 08:44:26 -05:00
}
2020-12-07 05:46:39 -05:00
fn get_diagnostic_message ( diagnostic : & diagnostics ::Diagnostic ) -> String {
if let Some ( message ) = diagnostic . message_text . clone ( ) {
message
} else if let Some ( message_chain ) = diagnostic . message_chain . clone ( ) {
message_chain . format_message ( 0 )
} else {
" [missing message] " . to_string ( )
}
}
2021-05-11 10:43:00 -04:00
fn to_lsp_range (
start : & diagnostics ::Position ,
end : & diagnostics ::Position ,
) -> lsp ::Range {
lsp ::Range {
start : start . into ( ) ,
end : end . into ( ) ,
}
}
2020-12-07 05:46:39 -05:00
fn to_lsp_related_information (
related_information : & Option < Vec < diagnostics ::Diagnostic > > ,
2021-01-29 14:34:33 -05:00
) -> Option < Vec < lsp ::DiagnosticRelatedInformation > > {
2021-03-25 14:17:37 -04:00
related_information . as_ref ( ) . map ( | related | {
related
. iter ( )
. filter_map ( | ri | {
if let ( Some ( source ) , Some ( start ) , Some ( end ) ) =
( & ri . source , & ri . start , & ri . end )
{
let uri = lsp ::Url ::parse ( & source ) . unwrap ( ) ;
Some ( lsp ::DiagnosticRelatedInformation {
location : lsp ::Location {
uri ,
range : to_lsp_range ( start , end ) ,
} ,
message : get_diagnostic_message ( & ri ) ,
} )
} else {
None
}
} )
. collect ( )
} )
2020-12-07 05:46:39 -05:00
}
fn ts_json_to_diagnostics (
2021-05-11 10:43:00 -04:00
diagnostics : Vec < diagnostics ::Diagnostic > ,
2021-01-29 14:34:33 -05:00
) -> Vec < lsp ::Diagnostic > {
2021-01-22 05:03:16 -05:00
diagnostics
. iter ( )
. filter_map ( | d | {
if let ( Some ( start ) , Some ( end ) ) = ( & d . start , & d . end ) {
2021-01-29 14:34:33 -05:00
Some ( lsp ::Diagnostic {
2021-01-22 05:03:16 -05:00
range : to_lsp_range ( start , end ) ,
severity : Some ( ( & d . category ) . into ( ) ) ,
2021-01-29 14:34:33 -05:00
code : Some ( lsp ::NumberOrString ::Number ( d . code as i32 ) ) ,
2021-01-22 05:03:16 -05:00
code_description : None ,
source : Some ( " deno-ts " . to_string ( ) ) ,
message : get_diagnostic_message ( d ) ,
related_information : to_lsp_related_information (
& d . related_information ,
) ,
tags : match d . code {
// These are codes that indicate the variable is unused.
2021-01-26 05:55:59 -05:00
2695 | 6133 | 6138 | 6192 | 6196 | 6198 | 6199 | 7027 | 7028 = > {
2021-01-29 14:34:33 -05:00
Some ( vec! [ lsp ::DiagnosticTag ::Unnecessary ] )
2021-01-22 05:03:16 -05:00
}
_ = > None ,
} ,
data : None ,
} )
} else {
None
}
} )
. collect ( )
2020-12-07 05:46:39 -05:00
}
2021-05-11 10:43:00 -04:00
async fn generate_lint_diagnostics (
snapshot : & language_server ::StateSnapshot ,
2021-05-07 07:05:32 -04:00
collection : Arc < Mutex < DiagnosticCollection > > ,
2020-12-07 05:46:39 -05:00
) -> Result < DiagnosticVec , AnyError > {
2021-05-11 10:43:00 -04:00
let documents = snapshot . documents . clone ( ) ;
2021-05-20 05:56:48 -04:00
let workspace_settings = snapshot . config . settings . workspace . clone ( ) ;
2021-05-11 10:43:00 -04:00
tokio ::task ::spawn ( async move {
let mut diagnostics_vec = Vec ::new ( ) ;
if workspace_settings . lint {
for specifier in documents . open_specifiers ( ) {
2021-06-02 06:29:58 -04:00
if ! documents . is_diagnosable ( specifier ) {
continue ;
}
2021-05-11 10:43:00 -04:00
let version = documents . version ( specifier ) ;
let current_version = collection
. lock ( )
. await
. get_version ( specifier , & DiagnosticSource ::DenoLint ) ;
2021-05-18 02:35:46 -04:00
let media_type = MediaType ::from ( specifier ) ;
2021-06-02 06:29:58 -04:00
if version ! = current_version {
2021-05-11 10:43:00 -04:00
if let Ok ( Some ( source_code ) ) = documents . content ( specifier ) {
if let Ok ( references ) = analysis ::get_lint_references (
specifier ,
& media_type ,
& source_code ,
) {
let diagnostics =
references . into_iter ( ) . map ( | r | r . to_diagnostic ( ) ) . collect ( ) ;
diagnostics_vec . push ( ( specifier . clone ( ) , version , diagnostics ) ) ;
} else {
diagnostics_vec . push ( ( specifier . clone ( ) , version , Vec ::new ( ) ) ) ;
}
} else {
error! ( " Missing file contents for: {} " , specifier ) ;
}
2021-05-09 21:16:04 -04:00
}
2021-05-07 07:05:32 -04:00
}
2021-01-22 05:03:16 -05:00
}
2021-05-11 10:43:00 -04:00
Ok ( diagnostics_vec )
} )
. await
. unwrap ( )
}
async fn generate_ts_diagnostics (
snapshot : & language_server ::StateSnapshot ,
collection : Arc < Mutex < DiagnosticCollection > > ,
ts_server : & tsc ::TsServer ,
) -> Result < DiagnosticVec , AnyError > {
let mut diagnostics_vec = Vec ::new ( ) ;
let specifiers : Vec < ModuleSpecifier > = {
let collection = collection . lock ( ) . await ;
snapshot
. documents
. open_specifiers ( )
. iter ( )
. filter_map ( | & s | {
2021-06-02 06:29:58 -04:00
if snapshot . documents . is_diagnosable ( s ) {
let version = snapshot . documents . version ( s ) ;
let current_version =
collection . get_version ( s , & DiagnosticSource ::TypeScript ) ;
if version ! = current_version {
Some ( s . clone ( ) )
} else {
None
}
2021-05-18 02:35:46 -04:00
} else {
None
2021-05-11 10:43:00 -04:00
}
} )
. collect ( )
} ;
2021-01-22 05:03:16 -05:00
if ! specifiers . is_empty ( ) {
let req = tsc ::RequestMethod ::GetDiagnostics ( specifiers ) ;
2021-05-11 10:43:00 -04:00
let ts_diagnostics_map : TsDiagnosticsMap =
ts_server . request ( snapshot . clone ( ) , req ) . await ? ;
for ( specifier_str , ts_diagnostics ) in ts_diagnostics_map {
let specifier = resolve_url ( & specifier_str ) ? ;
let version = snapshot . documents . version ( & specifier ) ;
diagnostics_vec . push ( (
2021-01-22 05:03:16 -05:00
specifier ,
version ,
ts_json_to_diagnostics ( ts_diagnostics ) ,
) ) ;
2020-12-07 05:46:39 -05:00
}
}
2021-05-11 10:43:00 -04:00
Ok ( diagnostics_vec )
2020-12-07 05:46:39 -05:00
}
2020-12-24 05:53:03 -05:00
2021-05-24 22:34:01 -04:00
fn diagnose_dependency (
diagnostics : & mut Vec < lsp ::Diagnostic > ,
documents : & DocumentCache ,
sources : & Sources ,
maybe_dependency : & Option < ResolvedDependency > ,
maybe_range : & Option < lsp ::Range > ,
) {
if let ( Some ( dep ) , Some ( range ) ) = ( maybe_dependency , * maybe_range ) {
match dep {
analysis ::ResolvedDependency ::Err ( err ) = > {
diagnostics . push ( lsp ::Diagnostic {
range ,
severity : Some ( lsp ::DiagnosticSeverity ::Error ) ,
code : Some ( err . as_code ( ) ) ,
code_description : None ,
source : Some ( " deno " . to_string ( ) ) ,
message : err . to_string ( ) ,
related_information : None ,
tags : None ,
data : None ,
} )
}
analysis ::ResolvedDependency ::Resolved ( specifier ) = > {
if ! ( documents . contains_key ( & specifier )
| | sources . contains_key ( & specifier ) )
{
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 ) ) ,
" data " = > ( Some ( lsp ::NumberOrString ::String ( " no-cache-data " . to_string ( ) ) ) , " Uncached data URL. " . to_string ( ) ) ,
" blob " = > ( Some ( lsp ::NumberOrString ::String ( " no-cache-blob " . to_string ( ) ) ) , " Uncached blob URL. " . to_string ( ) ) ,
_ = > ( Some ( lsp ::NumberOrString ::String ( " no-cache " . to_string ( ) ) ) , format! ( " Uncached or missing remote URL: \" {} \" . " , specifier ) ) ,
} ;
diagnostics . push ( lsp ::Diagnostic {
range ,
severity : Some ( lsp ::DiagnosticSeverity ::Error ) ,
code ,
source : Some ( " deno " . to_string ( ) ) ,
message ,
data : Some ( json! ( { " specifier " : specifier } ) ) ,
.. Default ::default ( )
} ) ;
} else if sources . contains_key ( & specifier ) {
if let Some ( message ) = sources . get_maybe_warning ( & specifier ) {
diagnostics . push ( lsp ::Diagnostic {
range ,
severity : Some ( lsp ::DiagnosticSeverity ::Warning ) ,
code : Some ( lsp ::NumberOrString ::String ( " deno-warn " . to_string ( ) ) ) ,
source : Some ( " deno " . to_string ( ) ) ,
message ,
.. Default ::default ( )
} )
}
}
}
}
}
}
2021-05-11 10:43:00 -04:00
/// 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 ,
2021-05-07 07:05:32 -04:00
collection : Arc < Mutex < DiagnosticCollection > > ,
2020-12-24 05:53:03 -05:00
) -> Result < DiagnosticVec , AnyError > {
2021-05-11 10:43:00 -04:00
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 ) {
2021-05-09 21:16:04 -04:00
continue ;
}
2021-05-11 10:43:00 -04:00
let version = documents . version ( specifier ) ;
let current_version = collection
. lock ( )
. await
. get_version ( specifier , & DiagnosticSource ::Deno ) ;
2020-12-24 05:53:03 -05:00
if version ! = current_version {
2021-05-11 10:43:00 -04:00
let mut diagnostics = Vec ::new ( ) ;
if let Some ( dependencies ) = documents . dependencies ( specifier ) {
for ( _ , dependency ) in dependencies {
2021-05-24 22:34:01 -04:00
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 ,
) ;
2020-12-24 05:53:03 -05:00
}
}
2021-05-11 10:43:00 -04:00
diagnostics_vec . push ( ( specifier . clone ( ) , version , diagnostics ) ) ;
2020-12-24 05:53:03 -05:00
}
}
2021-05-11 10:43:00 -04:00
Ok ( diagnostics_vec )
2020-12-24 05:53:03 -05:00
} )
. await
. unwrap ( )
}
2021-05-11 10:43:00 -04:00
/// Publishes diagnostics to the client.
async fn publish_diagnostics (
client : & lspower ::Client ,
collection : Arc < Mutex < DiagnosticCollection > > ,
snapshot : & language_server ::StateSnapshot ,
) {
let mut collection = collection . lock ( ) . await ;
if let Some ( changes ) = collection . take_changes ( ) {
for specifier in changes {
let mut diagnostics : Vec < lsp ::Diagnostic > =
2021-05-20 05:56:48 -04:00
if snapshot . config . settings . workspace . lint {
2021-05-11 10:43:00 -04:00
collection
. get ( & specifier , DiagnosticSource ::DenoLint )
. cloned ( )
. collect ( )
} else {
Vec ::new ( )
} ;
if snapshot . config . specifier_enabled ( & specifier ) {
diagnostics . extend (
collection
. get ( & specifier , DiagnosticSource ::TypeScript )
. cloned ( ) ,
) ;
diagnostics
. extend ( collection . get ( & specifier , DiagnosticSource ::Deno ) . cloned ( ) ) ;
}
let uri = specifier . clone ( ) ;
let version = snapshot . documents . version ( & specifier ) ;
client . publish_diagnostics ( uri , diagnostics , version ) . await ;
}
}
}
/// Updates diagnostics for any specifiers that don't have the correct version
/// generated and publishes the diagnostics to the client.
async fn update_diagnostics (
client : & lspower ::Client ,
collection : Arc < Mutex < DiagnosticCollection > > ,
snapshot : & language_server ::StateSnapshot ,
ts_server : & tsc ::TsServer ,
) {
let mark = snapshot . performance . mark ( " update_diagnostics " , None ::< ( ) > ) ;
let lint = async {
let mark = snapshot
. performance
. mark ( " update_diagnostics_lint " , None ::< ( ) > ) ;
let collection = collection . clone ( ) ;
let diagnostics = generate_lint_diagnostics ( snapshot , collection . clone ( ) )
. await
. map_err ( | err | {
error! ( " Error generating lint diagnostics: {} " , err ) ;
} )
. unwrap_or_default ( ) ;
{
let mut collection = collection . lock ( ) . await ;
for diagnostic_record in diagnostics {
collection . set ( DiagnosticSource ::DenoLint , diagnostic_record ) ;
}
}
publish_diagnostics ( client , collection , snapshot ) . await ;
snapshot . performance . measure ( mark ) ;
} ;
let ts = async {
let mark = snapshot
. performance
. mark ( " update_diagnostics_ts " , None ::< ( ) > ) ;
let collection = collection . clone ( ) ;
let diagnostics =
generate_ts_diagnostics ( snapshot , collection . clone ( ) , ts_server )
. await
. map_err ( | err | {
error! ( " Error generating TypeScript diagnostics: {} " , err ) ;
} )
. unwrap_or_default ( ) ;
{
let mut collection = collection . lock ( ) . await ;
for diagnostic_record in diagnostics {
collection . set ( DiagnosticSource ::TypeScript , diagnostic_record ) ;
}
}
publish_diagnostics ( client , collection , snapshot ) . await ;
snapshot . performance . measure ( mark ) ;
} ;
let deps = async {
let mark = snapshot
. performance
. mark ( " update_diagnostics_deps " , None ::< ( ) > ) ;
let collection = collection . clone ( ) ;
let diagnostics = generate_deps_diagnostics ( snapshot , collection . clone ( ) )
. await
. map_err ( | err | {
error! ( " Error generating Deno diagnostics: {} " , err ) ;
} )
. unwrap_or_default ( ) ;
{
let mut collection = collection . lock ( ) . await ;
for diagnostic_record in diagnostics {
collection . set ( DiagnosticSource ::Deno , diagnostic_record ) ;
}
}
publish_diagnostics ( client , collection , snapshot ) . await ;
snapshot . performance . measure ( mark ) ;
} ;
tokio ::join! ( lint , ts , deps ) ;
snapshot . performance . measure ( mark ) ;
}