2021-01-11 12:13:41 -05:00
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2020-12-21 08:44:26 -05:00
use deno_core ::error ::anyhow ;
use deno_core ::error ::AnyError ;
2021-02-17 13:47:18 -05:00
use deno_core ::resolve_url ;
2020-12-21 08:44:26 -05:00
use deno_core ::serde ::Deserialize ;
use deno_core ::serde ::Serialize ;
use deno_core ::serde_json ;
use deno_core ::serde_json ::json ;
use deno_core ::serde_json ::Value ;
use deno_core ::ModuleSpecifier ;
use dprint_plugin_typescript as dprint ;
2021-03-26 12:34:25 -04:00
use log ::error ;
use log ::info ;
use log ::warn ;
2020-12-29 23:17:17 -05:00
use lspower ::jsonrpc ::Error as LspError ;
use lspower ::jsonrpc ::Result as LspResult ;
2021-01-29 14:34:33 -05:00
use lspower ::lsp ::request ::* ;
use lspower ::lsp ::* ;
2020-12-21 08:44:26 -05:00
use lspower ::Client ;
2021-01-31 22:30:41 -05:00
use regex ::Regex ;
2021-02-04 13:53:02 -05:00
use serde_json ::from_value ;
2021-01-31 22:30:41 -05:00
use std ::cell ::RefCell ;
2020-12-21 08:44:26 -05:00
use std ::collections ::HashMap ;
use std ::env ;
use std ::path ::PathBuf ;
2021-01-31 22:30:41 -05:00
use std ::rc ::Rc ;
2020-12-21 08:44:26 -05:00
use std ::sync ::Arc ;
use tokio ::fs ;
use crate ::deno_dir ;
use crate ::import_map ::ImportMap ;
2021-02-09 17:46:12 -05:00
use crate ::media_type ::MediaType ;
2020-12-30 22:33:44 -05:00
use crate ::tsc_config ::parse_config ;
2020-12-21 08:44:26 -05:00
use crate ::tsc_config ::TsConfig ;
2021-02-09 17:46:12 -05:00
use super ::analysis ;
2021-02-05 15:10:53 -05:00
use super ::analysis ::ts_changes_to_edit ;
2021-02-04 13:53:02 -05:00
use super ::analysis ::CodeActionCollection ;
2021-02-05 15:10:53 -05:00
use super ::analysis ::CodeActionData ;
2021-01-31 22:30:41 -05:00
use super ::analysis ::CodeLensData ;
use super ::analysis ::CodeLensSource ;
2021-02-09 17:46:12 -05:00
use super ::analysis ::ResolvedDependency ;
2020-12-21 08:44:26 -05:00
use super ::capabilities ;
2021-03-24 20:13:37 -04:00
use super ::completions ;
2020-12-21 08:44:26 -05:00
use super ::config ::Config ;
use super ::diagnostics ;
use super ::diagnostics ::DiagnosticSource ;
2021-01-22 05:03:16 -05:00
use super ::documents ::DocumentCache ;
2021-01-26 19:32:49 -05:00
use super ::performance ::Performance ;
2021-04-08 21:27:27 -04:00
use super ::registries ;
2020-12-29 23:17:17 -05:00
use super ::sources ;
2020-12-21 08:44:26 -05:00
use super ::sources ::Sources ;
use super ::text ;
2021-01-22 05:03:16 -05:00
use super ::text ::LineIndex ;
2020-12-21 08:44:26 -05:00
use super ::tsc ;
2021-01-22 05:03:16 -05:00
use super ::tsc ::AssetDocument ;
2021-02-12 06:49:42 -05:00
use super ::tsc ::Assets ;
2020-12-21 08:44:26 -05:00
use super ::tsc ::TsServer ;
2021-02-17 23:37:05 -05:00
use super ::urls ;
2020-12-21 08:44:26 -05:00
2021-04-08 21:27:27 -04:00
pub const REGISTRIES_PATH : & str = " registries " ;
const SOURCES_PATH : & str = " deps " ;
2021-03-26 12:34:25 -04:00
lazy_static ::lazy_static! {
2021-02-08 05:45:10 -05:00
static ref ABSTRACT_MODIFIER : Regex = Regex ::new ( r "\babstract\b" ) . unwrap ( ) ;
2021-01-31 22:30:41 -05:00
static ref EXPORT_MODIFIER : Regex = Regex ::new ( r "\bexport\b" ) . unwrap ( ) ;
}
2020-12-21 08:44:26 -05:00
#[ derive(Debug, Clone) ]
2021-01-26 04:55:04 -05:00
pub struct LanguageServer ( Arc < tokio ::sync ::Mutex < Inner > > ) ;
2020-12-21 08:44:26 -05:00
#[ derive(Debug, Clone, Default) ]
pub struct StateSnapshot {
2021-02-12 06:49:42 -05:00
pub assets : Assets ,
2021-03-09 21:41:35 -05:00
pub config : Config ,
2021-01-25 18:47:12 -05:00
pub documents : DocumentCache ,
2021-04-08 21:27:27 -04:00
pub module_registries : registries ::ModuleRegistry ,
2021-02-11 23:17:48 -05:00
pub performance : Performance ,
2021-01-26 04:55:04 -05:00
pub sources : Sources ,
}
#[ derive(Debug) ]
2021-02-06 07:39:01 -05:00
pub ( crate ) struct Inner {
2021-02-04 13:53:02 -05:00
/// Cached versions of "fixed" assets that can either be inlined in Rust or
/// are part of the TypeScript snapshot and have to be fetched out.
2021-02-12 06:49:42 -05:00
assets : Assets ,
2021-02-04 13:53:02 -05:00
/// The LSP client that this LSP server is connected to.
2021-01-26 04:55:04 -05:00
client : Client ,
2021-02-04 13:53:02 -05:00
/// Configuration information.
2021-01-26 04:55:04 -05:00
config : Config ,
2021-03-09 21:41:35 -05:00
diagnostics_server : diagnostics ::DiagnosticsServer ,
2021-02-04 13:53:02 -05:00
/// The "in-memory" documents in the editor which can be updated and changed.
2021-01-26 19:32:49 -05:00
documents : DocumentCache ,
2021-04-08 21:27:27 -04:00
/// Handles module registries, which allow discovery of modules
module_registries : registries ::ModuleRegistry ,
/// The path to the module registries cache
module_registries_location : PathBuf ,
2021-02-04 13:53:02 -05:00
/// An optional URL which provides the location of a TypeScript configuration
/// file which will be used by the Deno LSP.
2021-01-26 04:55:04 -05:00
maybe_config_uri : Option < Url > ,
2021-02-04 13:53:02 -05:00
/// An optional import map which is used to resolve modules.
2021-02-12 06:49:42 -05:00
pub ( crate ) maybe_import_map : Option < ImportMap > ,
2021-02-04 13:53:02 -05:00
/// The URL for the import map which is used to determine relative imports.
2021-01-26 04:55:04 -05:00
maybe_import_map_uri : Option < Url > ,
2021-02-09 04:48:53 -05:00
/// A map of all the cached navigation trees.
navigation_trees : HashMap < ModuleSpecifier , tsc ::NavigationTree > ,
2021-02-04 13:53:02 -05:00
/// A collection of measurements which instrument that performance of the LSP.
2021-01-26 19:32:49 -05:00
performance : Performance ,
2021-02-04 13:53:02 -05:00
/// Cached sources that are read-only.
2021-01-26 19:32:49 -05:00
sources : Sources ,
2021-02-04 13:53:02 -05:00
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
ts_fixable_diagnostics : Vec < String > ,
/// An abstraction that handles interactions with TypeScript.
2021-03-09 21:41:35 -05:00
ts_server : Arc < TsServer > ,
2021-02-17 23:37:05 -05:00
/// A map of specifiers and URLs used to translate over the LSP.
pub url_map : urls ::LspUrlMap ,
2020-12-21 08:44:26 -05:00
}
impl LanguageServer {
pub fn new ( client : Client ) -> Self {
2021-01-26 04:55:04 -05:00
Self ( Arc ::new ( tokio ::sync ::Mutex ::new ( Inner ::new ( client ) ) ) )
}
}
impl Inner {
fn new ( client : Client ) -> Self {
2020-12-21 08:44:26 -05:00
let maybe_custom_root = env ::var ( " DENO_DIR " ) . map ( String ::into ) . ok ( ) ;
let dir = deno_dir ::DenoDir ::new ( maybe_custom_root )
. expect ( " could not access DENO_DIR " ) ;
2021-04-08 21:27:27 -04:00
let module_registries_location = dir . root . join ( REGISTRIES_PATH ) ;
let module_registries =
registries ::ModuleRegistry ::new ( & module_registries_location ) ;
let sources_location = dir . root . join ( SOURCES_PATH ) ;
let sources = Sources ::new ( & sources_location ) ;
2021-03-09 21:41:35 -05:00
let ts_server = Arc ::new ( TsServer ::new ( ) ) ;
let performance = Performance ::default ( ) ;
let diagnostics_server = diagnostics ::DiagnosticsServer ::new ( ) ;
2020-12-21 08:44:26 -05:00
2021-01-26 04:55:04 -05:00
Self {
2020-12-21 08:44:26 -05:00
assets : Default ::default ( ) ,
client ,
config : Default ::default ( ) ,
2021-03-09 21:41:35 -05:00
diagnostics_server ,
2021-01-26 19:32:49 -05:00
documents : Default ::default ( ) ,
2020-12-30 22:33:44 -05:00
maybe_config_uri : Default ::default ( ) ,
2020-12-21 08:44:26 -05:00
maybe_import_map : Default ::default ( ) ,
maybe_import_map_uri : Default ::default ( ) ,
2021-04-08 21:27:27 -04:00
module_registries ,
module_registries_location ,
2021-02-09 04:48:53 -05:00
navigation_trees : Default ::default ( ) ,
2021-03-09 21:41:35 -05:00
performance ,
2021-01-26 19:32:49 -05:00
sources ,
2021-02-04 13:53:02 -05:00
ts_fixable_diagnostics : Default ::default ( ) ,
2021-03-09 21:41:35 -05:00
ts_server ,
2021-02-17 23:37:05 -05:00
url_map : Default ::default ( ) ,
2020-12-21 08:44:26 -05:00
}
}
2021-02-09 17:46:12 -05:00
/// Analyzes dependencies of a document that has been opened in the editor and
/// sets the dependencies property on the document.
fn analyze_dependencies (
& mut self ,
specifier : & ModuleSpecifier ,
source : & str ,
) {
2021-03-24 20:13:37 -04:00
let media_type = MediaType ::from ( specifier ) ;
if let Ok ( parsed_module ) =
analysis ::parse_module ( specifier , source , & media_type )
{
let ( mut deps , _ ) = analysis ::analyze_dependencies (
specifier ,
& media_type ,
& parsed_module ,
& self . maybe_import_map ,
) ;
2021-02-09 17:46:12 -05:00
for ( _ , dep ) in deps . iter_mut ( ) {
if dep . maybe_type . is_none ( ) {
if let Some ( ResolvedDependency ::Resolved ( resolved ) ) = & dep . maybe_code
{
dep . maybe_type = self . sources . get_maybe_types ( resolved ) ;
}
}
}
if let Err ( err ) = self . documents . set_dependencies ( specifier , Some ( deps ) ) {
error! ( " {} " , err ) ;
}
}
}
2020-12-22 00:42:32 -05:00
fn enabled ( & self ) -> bool {
2021-01-26 04:55:04 -05:00
self . config . settings . enable
2020-12-22 00:42:32 -05:00
}
2021-01-22 05:03:16 -05:00
/// Searches assets, open documents and external sources for a line_index,
/// which might be performed asynchronously, hydrating in memory caches for
/// subsequent requests.
2021-02-06 07:39:01 -05:00
pub ( crate ) async fn get_line_index (
& mut self ,
2020-12-22 05:21:18 -05:00
specifier : ModuleSpecifier ,
2021-01-22 05:03:16 -05:00
) -> Result < LineIndex , AnyError > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " get_line_index " ) ;
2021-02-17 13:47:18 -05:00
let result = if specifier . scheme ( ) = = " asset " {
2021-02-06 07:39:01 -05:00
if let Some ( asset ) = self . get_asset ( & specifier ) . await ? {
Ok ( asset . line_index )
2020-12-21 08:44:26 -05:00
} else {
2021-02-06 07:39:01 -05:00
Err ( anyhow! ( " asset is missing: {} " , specifier ) )
2020-12-22 05:21:18 -05:00
}
2021-01-26 04:55:04 -05:00
} else if let Some ( line_index ) = self . documents . line_index ( & specifier ) {
2021-01-22 05:03:16 -05:00
Ok ( line_index )
2021-01-26 04:55:04 -05:00
} else if let Some ( line_index ) = self . sources . get_line_index ( & specifier ) {
2021-01-22 05:03:16 -05:00
Ok ( line_index )
2020-12-21 08:44:26 -05:00
} else {
2021-01-22 05:03:16 -05:00
Err ( anyhow! ( " Unable to find line index for: {} " , specifier ) )
2021-01-26 19:32:49 -05:00
} ;
self . performance . measure ( mark ) ;
result
2021-01-22 05:03:16 -05:00
}
/// Only searches already cached assets and documents for a line index. If
/// the line index cannot be found, `None` is returned.
2021-01-26 04:55:04 -05:00
fn get_line_index_sync (
2021-02-11 23:17:48 -05:00
& self ,
2021-01-22 05:03:16 -05:00
specifier : & ModuleSpecifier ,
) -> Option < LineIndex > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " get_line_index_sync " ) ;
2021-02-17 13:47:18 -05:00
let maybe_line_index = if specifier . scheme ( ) = = " asset " {
2021-01-26 04:55:04 -05:00
if let Some ( Some ( asset ) ) = self . assets . get ( specifier ) {
2021-01-22 05:03:16 -05:00
Some ( asset . line_index . clone ( ) )
2020-12-22 05:21:18 -05:00
} else {
2021-01-22 05:03:16 -05:00
None
2020-12-22 05:21:18 -05:00
}
2021-01-22 05:03:16 -05:00
} else {
2021-01-26 04:55:04 -05:00
let documents = & self . documents ;
2021-02-12 06:49:42 -05:00
if documents . contains_key ( specifier ) {
2021-01-22 05:03:16 -05:00
documents . line_index ( specifier )
} else {
2021-01-26 04:55:04 -05:00
self . sources . get_line_index ( specifier )
2021-01-22 05:03:16 -05:00
}
2021-01-26 19:32:49 -05:00
} ;
self . performance . measure ( mark ) ;
maybe_line_index
2020-12-21 08:44:26 -05:00
}
2021-04-02 02:21:07 -04:00
// TODO(@kitsonk) we really should find a better way to just return the
// content as a `&str`, or be able to get the byte at a particular offset
// which is all that this API that is consuming it is trying to do at the
// moment
/// Searches already cached assets and documents and returns its text
/// content. If not found, `None` is returned.
fn get_text_content ( & self , specifier : & ModuleSpecifier ) -> Option < String > {
if specifier . scheme ( ) = = " asset " {
self
. assets
. get ( specifier )
. map ( | o | o . clone ( ) . map ( | a | a . text ) ) ?
} else if self . documents . contains_key ( specifier ) {
self . documents . content ( specifier ) . unwrap ( )
} else {
self . sources . get_source ( specifier )
}
}
2021-01-31 22:30:41 -05:00
async fn get_navigation_tree (
& mut self ,
specifier : & ModuleSpecifier ,
) -> Result < tsc ::NavigationTree , AnyError > {
2021-02-09 04:48:53 -05:00
let mark = self . performance . mark ( " get_navigation_tree " ) ;
if let Some ( navigation_tree ) = self . navigation_trees . get ( specifier ) {
self . performance . measure ( mark ) ;
Ok ( navigation_tree . clone ( ) )
2021-01-31 22:30:41 -05:00
} else {
2021-02-24 22:15:55 -05:00
let navigation_tree : tsc ::NavigationTree = self
2021-02-09 04:48:53 -05:00
. ts_server
. request (
self . snapshot ( ) ,
tsc ::RequestMethod ::GetNavigationTree ( specifier . clone ( ) ) ,
)
. await ? ;
self
. navigation_trees
. insert ( specifier . clone ( ) , navigation_tree . clone ( ) ) ;
self . performance . measure ( mark ) ;
Ok ( navigation_tree )
2021-01-31 22:30:41 -05:00
}
}
2021-03-09 21:41:35 -05:00
pub ( crate ) fn snapshot ( & self ) -> StateSnapshot {
2020-12-21 08:44:26 -05:00
StateSnapshot {
assets : self . assets . clone ( ) ,
2021-03-09 21:41:35 -05:00
config : self . config . clone ( ) ,
2021-01-26 04:55:04 -05:00
documents : self . documents . clone ( ) ,
2021-04-08 21:27:27 -04:00
module_registries : self . module_registries . clone ( ) ,
2021-02-11 23:17:48 -05:00
performance : self . performance . clone ( ) ,
2020-12-21 08:44:26 -05:00
sources : self . sources . clone ( ) ,
}
}
2021-01-26 19:32:49 -05:00
pub async fn update_import_map ( & mut self ) -> Result < ( ) , AnyError > {
let mark = self . performance . mark ( " update_import_map " ) ;
2020-12-22 05:21:18 -05:00
let ( maybe_import_map , maybe_root_uri ) = {
2021-01-26 04:55:04 -05:00
let config = & self . config ;
2020-12-22 05:21:18 -05:00
( config . settings . import_map . clone ( ) , config . root_uri . clone ( ) )
} ;
if let Some ( import_map_str ) = & maybe_import_map {
2020-12-30 22:33:44 -05:00
info! ( " Updating import map from: \" {} \" " , import_map_str ) ;
2020-12-22 05:21:18 -05:00
let import_map_url = if let Ok ( url ) = Url ::from_file_path ( import_map_str )
2020-12-21 08:44:26 -05:00
{
2020-12-22 05:21:18 -05:00
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 import_map_path = root_path . join ( import_map_str ) ;
Url ::from_file_path ( import_map_path ) . map_err ( | _ | {
anyhow! ( " Bad file path for import map: {:?} " , import_map_str )
} )
2020-12-21 08:44:26 -05:00
} else {
2020-12-22 05:21:18 -05:00
Err ( anyhow! (
" The path to the import map ( \" {} \" ) is not resolvable. " ,
import_map_str
) )
} ? ;
let import_map_path = import_map_url
. to_file_path ( )
. map_err ( | _ | anyhow! ( " Bad file path. " ) ) ? ;
let import_map_json =
fs ::read_to_string ( import_map_path ) . await . map_err ( | err | {
anyhow! (
" Failed to load the import map at: {}. [{}] " ,
import_map_url ,
err
)
} ) ? ;
let import_map =
ImportMap ::from_json ( & import_map_url . to_string ( ) , & import_map_json ) ? ;
2021-01-26 04:55:04 -05:00
self . maybe_import_map_uri = Some ( import_map_url ) ;
self . maybe_import_map = Some ( import_map ) ;
2020-12-21 08:44:26 -05:00
} else {
2021-01-26 04:55:04 -05:00
self . maybe_import_map = None ;
2020-12-22 05:21:18 -05:00
}
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-22 05:21:18 -05:00
Ok ( ( ) )
}
2021-04-08 21:27:27 -04:00
async fn update_registries ( & mut self ) -> Result < ( ) , AnyError > {
let mark = self . performance . mark ( " update_registries " ) ;
for ( registry , enabled ) in self . config . settings . suggest . imports . hosts . iter ( )
{
if * enabled {
info! ( " Enabling auto complete registry for: {} " , registry ) ;
self . module_registries . enable ( registry ) . await ? ;
} else {
info! ( " Disabling auto complete registry for: {} " , registry ) ;
self . module_registries . disable ( registry ) . await ? ;
}
}
self . performance . measure ( mark ) ;
Ok ( ( ) )
}
2021-01-26 04:55:04 -05:00
async fn update_tsconfig ( & mut self ) -> Result < ( ) , AnyError > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " update_tsconfig " ) ;
2020-12-22 05:21:18 -05:00
let mut tsconfig = TsConfig ::new ( json! ( {
" allowJs " : true ,
2021-02-05 06:01:48 -05:00
" esModuleInterop " : true ,
2020-12-22 05:21:18 -05:00
" experimentalDecorators " : true ,
" isolatedModules " : true ,
2021-02-05 06:01:48 -05:00
" jsx " : " react " ,
2020-12-22 05:21:18 -05:00
" lib " : [ " deno.ns " , " deno.window " ] ,
" module " : " esnext " ,
" noEmit " : true ,
" strict " : true ,
" target " : " esnext " ,
2021-04-10 17:56:40 -04:00
" useDefineForClassFields " : true ,
2020-12-22 05:21:18 -05:00
} ) ) ;
2020-12-30 22:33:44 -05:00
let ( maybe_config , maybe_root_uri ) = {
2021-01-26 04:55:04 -05:00
let config = & self . config ;
2020-12-22 05:21:18 -05:00
if config . settings . unstable {
let unstable_libs = json! ( {
" lib " : [ " deno.ns " , " deno.window " , " deno.unstable " ]
} ) ;
tsconfig . merge ( & unstable_libs ) ;
2020-12-21 08:44:26 -05:00
}
2020-12-30 22:33:44 -05:00
( 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 ) ;
2021-01-26 04:55:04 -05:00
self . maybe_config_uri = Some ( config_url ) ;
2020-12-30 22:33:44 -05:00
if let Some ( ignored_options ) = maybe_ignored_options {
// TODO(@kitsonk) turn these into diagnostics that can be sent to the
// client
warn! ( " {} " , ignored_options ) ;
}
2020-12-22 05:21:18 -05:00
}
2021-02-24 22:15:55 -05:00
let _ok : bool = self
2020-12-22 05:21:18 -05:00
. ts_server
. request ( self . snapshot ( ) , tsc ::RequestMethod ::Configure ( tsconfig ) )
. await ? ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-22 05:21:18 -05:00
Ok ( ( ) )
2020-12-21 08:44:26 -05:00
}
2021-02-06 07:39:01 -05:00
pub ( crate ) fn document_version (
2021-02-11 23:17:48 -05:00
& self ,
2021-02-06 07:39:01 -05:00
specifier : ModuleSpecifier ,
) -> Option < i32 > {
self . documents . version ( & specifier )
}
2021-02-06 07:39:01 -05:00
async fn get_asset (
& mut self ,
specifier : & ModuleSpecifier ,
) -> Result < Option < AssetDocument > , AnyError > {
2021-02-06 07:39:01 -05:00
if let Some ( maybe_asset ) = self . assets . get ( specifier ) {
return Ok ( maybe_asset . clone ( ) ) ;
} else {
let maybe_asset =
2021-02-12 06:49:42 -05:00
tsc ::get_asset ( & specifier , & self . ts_server , self . snapshot ( ) ) . await ? ;
2021-02-06 07:39:01 -05:00
self . assets . insert ( specifier . clone ( ) , maybe_asset . clone ( ) ) ;
Ok ( maybe_asset )
}
2021-02-06 07:39:01 -05:00
}
2020-12-21 08:44:26 -05:00
}
2021-01-26 04:55:04 -05:00
// lspower::LanguageServer methods. This file's LanguageServer delegates to us.
impl Inner {
2020-12-21 08:44:26 -05:00
async fn initialize (
2021-01-26 04:55:04 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
params : InitializeParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < InitializeResult > {
2020-12-21 08:44:26 -05:00
info! ( " Starting Deno language server... " ) ;
2021-02-04 13:53:02 -05:00
let mark = self . performance . mark ( " initialize " ) ;
2020-12-21 08:44:26 -05:00
let capabilities = capabilities ::server_capabilities ( & params . capabilities ) ;
let version = format! (
" {} ({}, {}) " ,
crate ::version ::deno ( ) ,
env! ( " PROFILE " ) ,
env! ( " TARGET " )
) ;
info! ( " version: {} " , version ) ;
let server_info = ServerInfo {
name : " deno-language-server " . to_string ( ) ,
version : Some ( version ) ,
} ;
if let Some ( client_info ) = params . client_info {
info! (
" Connected to \" {} \" {} " ,
client_info . name ,
client_info . version . unwrap_or_default ( ) ,
) ;
}
{
2021-01-26 04:55:04 -05:00
let config = & mut self . config ;
2020-12-21 08:44:26 -05:00
config . root_uri = params . root_uri ;
if let Some ( value ) = params . initialization_options {
config . update ( value ) ? ;
}
config . update_capabilities ( & params . capabilities ) ;
}
2020-12-22 05:21:18 -05:00
if let Err ( err ) = self . update_tsconfig ( ) . await {
warn! ( " Updating tsconfig has errored: {} " , err ) ;
}
2020-12-21 08:44:26 -05:00
2021-02-04 13:53:02 -05:00
if capabilities . code_action_provider . is_some ( ) {
2021-02-24 22:15:55 -05:00
let fixable_diagnostics : Vec < String > = self
2021-02-04 13:53:02 -05:00
. ts_server
. request ( self . snapshot ( ) , tsc ::RequestMethod ::GetSupportedCodeFixes )
. await
. map_err ( | err | {
error! ( " Unable to get fixable diagnostics: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
self . ts_fixable_diagnostics = fixable_diagnostics ;
}
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( InitializeResult {
capabilities ,
server_info : Some ( server_info ) ,
} )
}
2021-01-26 04:55:04 -05:00
async fn initialized ( & mut self , _ : InitializedParams ) {
2020-12-21 08:44:26 -05:00
// Check to see if we need to setup the import map
if let Err ( err ) = self . update_import_map ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
2021-04-08 21:27:27 -04:00
// Check to see if we need to setup any module registries
if let Err ( err ) = self . update_registries ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
2020-12-21 08:44:26 -05:00
2021-01-04 16:52:20 -05:00
if self
. config
. client_capabilities
. workspace_did_change_watched_files
2020-12-21 08:44:26 -05:00
{
2021-01-04 16:52:20 -05:00
// we are going to watch all the JSON files in the workspace, and the
// notification handler will pick up any of the changes of those files we
// are interested in.
let watch_registration_options =
DidChangeWatchedFilesRegistrationOptions {
watchers : vec ! [ FileSystemWatcher {
glob_pattern : " **/*.json " . to_string ( ) ,
kind : Some ( WatchKind ::Change ) ,
} ] ,
} ;
let registration = Registration {
id : " workspace/didChangeWatchedFiles " . to_string ( ) ,
method : " workspace/didChangeWatchedFiles " . to_string ( ) ,
register_options : Some (
serde_json ::to_value ( watch_registration_options ) . unwrap ( ) ,
) ,
} ;
if let Err ( err ) =
self . client . register_capability ( vec! [ registration ] ) . await
{
warn! ( " Client errored on capabilities. \n {} " , err ) ;
}
2020-12-21 08:44:26 -05:00
}
info! ( " Server ready. " ) ;
}
2020-12-29 23:17:17 -05:00
async fn shutdown ( & self ) -> LspResult < ( ) > {
2020-12-21 08:44:26 -05:00
Ok ( ( ) )
}
2021-01-26 04:55:04 -05:00
async fn did_open ( & mut self , params : DidOpenTextDocumentParams ) {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " did_open " ) ;
2020-12-21 08:44:26 -05:00
if params . text_document . uri . scheme ( ) = = " deno " {
// we can ignore virtual text documents opening, as they don't need to
// be tracked in memory, as they are static assets that won't change
// already managed by the language service
return ;
}
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-01-26 04:55:04 -05:00
self . documents . open (
2021-01-22 05:03:16 -05:00
specifier . clone ( ) ,
params . text_document . version ,
2021-02-09 17:46:12 -05:00
& params . text_document . text ,
2021-01-22 05:03:16 -05:00
) ;
2021-02-09 17:46:12 -05:00
self . analyze_dependencies ( & specifier , & params . text_document . text ) ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-02-09 17:46:12 -05:00
2021-03-09 21:41:35 -05:00
if let Err ( err ) = self . diagnostics_server . update ( ) {
2021-01-22 05:03:16 -05:00
error! ( " {} " , err ) ;
}
2020-12-21 08:44:26 -05:00
}
2021-01-26 04:55:04 -05:00
async fn did_change ( & mut self , params : DidChangeTextDocumentParams ) {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " did_change " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-02-09 17:46:12 -05:00
match self . documents . change (
2021-01-22 05:03:16 -05:00
& specifier ,
params . text_document . version ,
params . content_changes ,
) {
2021-02-09 17:46:12 -05:00
Ok ( Some ( source ) ) = > self . analyze_dependencies ( & specifier , & source ) ,
Ok ( _ ) = > error! ( " No content returned from change. " ) ,
Err ( err ) = > error! ( " {} " , err ) ,
2020-12-21 08:44:26 -05:00
}
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-02-09 17:46:12 -05:00
2021-03-09 21:41:35 -05:00
if let Err ( err ) = self . diagnostics_server . update ( ) {
2021-01-22 05:03:16 -05:00
error! ( " {} " , err ) ;
}
2020-12-21 08:44:26 -05:00
}
2021-01-26 04:55:04 -05:00
async fn did_close ( & mut self , params : DidCloseTextDocumentParams ) {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " did_close " ) ;
2020-12-21 08:44:26 -05:00
if params . text_document . uri . scheme ( ) = = " deno " {
// we can ignore virtual text documents opening, as they don't need to
// be tracked in memory, as they are static assets that won't change
// already managed by the language service
return ;
}
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-01-26 04:55:04 -05:00
self . documents . close ( & specifier ) ;
2021-02-09 04:48:53 -05:00
self . navigation_trees . remove ( & specifier ) ;
2021-01-22 05:03:16 -05:00
2021-02-09 04:48:53 -05:00
self . performance . measure ( mark ) ;
2021-03-09 21:41:35 -05:00
if let Err ( err ) = self . diagnostics_server . update ( ) {
2021-01-22 05:03:16 -05:00
error! ( " {} " , err ) ;
2020-12-21 08:44:26 -05:00
}
}
async fn did_change_configuration (
2021-01-26 04:55:04 -05:00
& mut self ,
2021-01-04 16:52:20 -05:00
params : DidChangeConfigurationParams ,
2020-12-21 08:44:26 -05:00
) {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " did_change_configuration " ) ;
2021-01-26 04:55:04 -05:00
let config = if self . config . client_capabilities . workspace_configuration {
2021-01-04 16:52:20 -05:00
self
. client
. configuration ( vec! [ ConfigurationItem {
scope_uri : None ,
section : Some ( " deno " . to_string ( ) ) ,
} ] )
. await
. map ( | vec | vec . get ( 0 ) . cloned ( ) )
. unwrap_or_else ( | err | {
2021-02-09 04:48:53 -05:00
error! ( " failed to fetch the extension settings {} " , err ) ;
2021-01-04 16:52:20 -05:00
None
} )
} else {
params
. settings
. as_object ( )
. map ( | settings | settings . get ( " deno " ) )
. flatten ( )
. cloned ( )
} ;
2020-12-21 08:44:26 -05:00
2021-01-04 16:52:20 -05:00
if let Some ( config ) = config {
2021-01-26 04:55:04 -05:00
if let Err ( err ) = self . config . update ( config ) {
2021-01-04 16:52:20 -05:00
error! ( " failed to update settings: {} " , err ) ;
2020-12-21 08:44:26 -05:00
}
2021-01-04 16:52:20 -05:00
if let Err ( err ) = self . update_import_map ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
2021-04-08 21:27:27 -04:00
if let Err ( err ) = self . update_registries ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
2021-01-04 16:52:20 -05:00
if let Err ( err ) = self . update_tsconfig ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
2021-03-09 21:41:35 -05:00
if let Err ( err ) = self . diagnostics_server . update ( ) {
2021-02-08 05:45:46 -05:00
error! ( " {} " , err ) ;
}
2021-01-04 16:52:20 -05:00
} else {
error! ( " received empty extension settings from the client " ) ;
2020-12-21 08:44:26 -05:00
}
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
}
async fn did_change_watched_files (
2021-01-26 04:55:04 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
params : DidChangeWatchedFilesParams ,
) {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " did_change_watched_files " ) ;
2020-12-21 08:44:26 -05:00
// if the current import map has changed, we need to reload it
2021-01-26 04:55:04 -05:00
if let Some ( import_map_uri ) = & self . maybe_import_map_uri {
if params . changes . iter ( ) . any ( | fe | * import_map_uri = = fe . uri ) {
2020-12-21 08:44:26 -05:00
if let Err ( err ) = self . update_import_map ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
}
}
2020-12-30 22:33:44 -05:00
// if the current tsconfig has changed, we need to reload it
2021-01-26 04:55:04 -05:00
if let Some ( config_uri ) = & self . maybe_config_uri {
if params . changes . iter ( ) . any ( | fe | * config_uri = = fe . uri ) {
2020-12-30 22:33:44 -05:00
if let Err ( err ) = self . update_tsconfig ( ) . await {
self
. client
. show_message ( MessageType ::Warning , err . to_string ( ) )
. await ;
}
}
}
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
}
2021-04-19 21:29:27 -04:00
async fn document_symbol (
& self ,
params : DocumentSymbolParams ,
) -> LspResult < Option < DocumentSymbolResponse > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-04-20 22:41:24 -04:00
let mark = self . performance . mark ( " document_symbol " ) ;
2021-04-19 21:29:27 -04:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::GetNavigationTree ( specifier ) ;
let navigation_tree : tsc ::NavigationTree = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let response = if let Some ( child_items ) = navigation_tree . child_items {
let mut document_symbols = Vec ::< DocumentSymbol > ::new ( ) ;
for item in child_items {
item . collect_document_symbols ( & line_index , & mut document_symbols ) ;
}
Some ( DocumentSymbolResponse ::Nested ( document_symbols ) )
} else {
None
} ;
self . performance . measure ( mark ) ;
Ok ( response )
}
2020-12-21 08:44:26 -05:00
async fn formatting (
& self ,
params : DocumentFormattingParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < Vec < TextEdit > > > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " formatting " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-01-22 05:03:16 -05:00
let file_text = self
. documents
. content ( & specifier )
. map_err ( | _ | {
LspError ::invalid_params (
" The specified file could not be found in memory. " ,
)
} ) ?
. unwrap ( ) ;
2021-01-26 15:50:13 -05:00
let line_index = self . documents . line_index ( & specifier ) ;
2020-12-21 08:44:26 -05:00
let file_path =
if let Ok ( file_path ) = params . text_document . uri . to_file_path ( ) {
file_path
} else {
PathBuf ::from ( params . text_document . uri . path ( ) )
} ;
// TODO(lucacasonato): handle error properly
let text_edits = tokio ::task ::spawn_blocking ( move | | {
let config = dprint ::configuration ::ConfigurationBuilder ::new ( )
. deno ( )
. build ( ) ;
// TODO(@kitsonk) this could be handled better in `cli/tools/fmt.rs` in the
// future.
match dprint ::format_text ( & file_path , & file_text , & config ) {
2021-01-26 15:50:13 -05:00
Ok ( new_text ) = > {
Some ( text ::get_edits ( & file_text , & new_text , line_index ) )
}
2020-12-21 08:44:26 -05:00
Err ( err ) = > {
warn! ( " Format error: {} " , err ) ;
None
}
}
} )
. await
. unwrap ( ) ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
if let Some ( text_edits ) = text_edits {
if text_edits . is_empty ( ) {
Ok ( None )
} else {
Ok ( Some ( text_edits ) )
}
} else {
2021-01-26 15:50:13 -05:00
self . client . show_message ( MessageType ::Warning , format! ( " Unable to format \" {} \" . Likely due to unrecoverable syntax errors in the file. " , specifier ) ) . await ;
2020-12-21 08:44:26 -05:00
Ok ( None )
}
}
2021-02-11 23:17:48 -05:00
async fn hover ( & self , params : HoverParams ) -> LspResult < Option < Hover > > {
2020-12-22 00:42:32 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " hover " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
2021-01-22 05:03:16 -05:00
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2020-12-21 08:44:26 -05:00
let req = tsc ::RequestMethod ::GetQuickInfo ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
2020-12-21 08:44:26 -05:00
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_quick_info : Option < tsc ::QuickInfo > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get quick info: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-21 08:44:26 -05:00
if let Some ( quick_info ) = maybe_quick_info {
2021-01-22 05:03:16 -05:00
let hover = quick_info . to_hover ( & line_index ) ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-01-22 05:03:16 -05:00
Ok ( Some ( hover ) )
2020-12-21 08:44:26 -05:00
} else {
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( None )
}
}
2021-02-04 13:53:02 -05:00
async fn code_action (
& mut self ,
params : CodeActionParams ,
) -> LspResult < Option < CodeActionResponse > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " code_action " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-02-04 13:53:02 -05:00
let fixable_diagnostics : Vec < & Diagnostic > = params
. context
. diagnostics
. iter ( )
. filter ( | d | match & d . source {
Some ( source ) = > match source . as_str ( ) {
" deno-ts " = > match & d . code {
Some ( NumberOrString ::String ( code ) ) = > {
self . ts_fixable_diagnostics . contains ( code )
}
Some ( NumberOrString ::Number ( code ) ) = > {
self . ts_fixable_diagnostics . contains ( & code . to_string ( ) )
}
_ = > false ,
} ,
2021-02-11 23:17:48 -05:00
" deno " = > match & d . code {
2021-02-17 23:37:05 -05:00
Some ( NumberOrString ::String ( code ) ) = > {
code = = " no-cache " | | code = = " no-cache-data "
}
2021-02-11 23:17:48 -05:00
_ = > false ,
} ,
2021-02-04 13:53:02 -05:00
_ = > false ,
} ,
None = > false ,
} )
. collect ( ) ;
if fixable_diagnostics . is_empty ( ) {
self . performance . measure ( mark ) ;
return Ok ( None ) ;
}
let line_index = self . get_line_index_sync ( & specifier ) . unwrap ( ) ;
2021-02-11 23:17:48 -05:00
let mut code_actions = CodeActionCollection ::default ( ) ;
2021-03-09 21:41:35 -05:00
let file_diagnostics = self
. diagnostics_server
. get ( specifier . clone ( ) , DiagnosticSource ::TypeScript )
. await
. map_err ( | err | {
error! ( " Unable to get diagnostics: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-02-04 13:53:02 -05:00
for diagnostic in & fixable_diagnostics {
2021-02-11 23:17:48 -05:00
match diagnostic . source . as_deref ( ) {
Some ( " deno-ts " ) = > {
let code = match diagnostic . code . as_ref ( ) . unwrap ( ) {
NumberOrString ::String ( code ) = > code . to_string ( ) ,
NumberOrString ::Number ( code ) = > code . to_string ( ) ,
} ;
let codes = vec! [ code ] ;
let req = tsc ::RequestMethod ::GetCodeFixes ( (
specifier . clone ( ) ,
line_index . offset_tsc ( diagnostic . range . start ) ? ,
line_index . offset_tsc ( diagnostic . range . end ) ? ,
codes ,
) ) ;
2021-02-24 22:15:55 -05:00
let actions : Vec < tsc ::CodeFixAction > =
2021-04-07 05:47:31 -04:00
match self . ts_server . request ( self . snapshot ( ) , req ) . await {
Ok ( items ) = > items ,
Err ( err ) = > {
// sometimes tsc reports errors when retrieving code actions
// because they don't reflect the current state of the document
// so we will log them to the output, but we won't send an error
// message back to the client.
2021-02-11 23:17:48 -05:00
error! ( " Error getting actions from TypeScript: {} " , err ) ;
2021-04-07 05:47:31 -04:00
Vec ::new ( )
}
} ;
2021-02-11 23:17:48 -05:00
for action in actions {
code_actions
. add_ts_fix_action ( & action , diagnostic , self )
. await
. map_err ( | err | {
error! ( " Unable to convert fix: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
if code_actions . is_fix_all_action (
& action ,
diagnostic ,
& file_diagnostics ,
) {
code_actions
. add_ts_fix_all_action ( & action , & specifier , diagnostic ) ;
}
}
2021-02-04 13:53:02 -05:00
}
2021-02-11 23:17:48 -05:00
Some ( " deno " ) = > {
code_actions
. add_deno_fix_action ( diagnostic )
. map_err ( | err | {
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ?
}
_ = > ( ) ,
2021-02-04 13:53:02 -05:00
}
}
code_actions . set_preferred_fixes ( ) ;
let code_action_response = code_actions . get_response ( ) ;
self . performance . measure ( mark ) ;
Ok ( Some ( code_action_response ) )
}
2021-02-05 15:10:53 -05:00
async fn code_action_resolve (
2021-02-06 07:39:01 -05:00
& mut self ,
2021-02-05 15:10:53 -05:00
params : CodeAction ,
) -> LspResult < CodeAction > {
let mark = self . performance . mark ( " code_action_resolve " ) ;
2021-02-24 22:15:55 -05:00
let result = if let Some ( data ) = params . data . clone ( ) {
let code_action_data : CodeActionData =
from_value ( data ) . map_err ( | err | {
error! ( " Unable to decode code action data: {} " , err ) ;
LspError ::invalid_params ( " The CodeAction's data is invalid. " )
} ) ? ;
let req = tsc ::RequestMethod ::GetCombinedCodeFix ( (
code_action_data . specifier ,
json! ( code_action_data . fix_id . clone ( ) ) ,
) ) ;
let combined_code_actions : tsc ::CombinedCodeActions = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get combined fix from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
if combined_code_actions . commands . is_some ( ) {
error! ( " Deno does not support code actions with commands. " ) ;
Err ( LspError ::invalid_request ( ) )
2021-02-05 15:10:53 -05:00
} else {
2021-02-24 22:15:55 -05:00
let mut code_action = params . clone ( ) ;
code_action . edit =
ts_changes_to_edit ( & combined_code_actions . changes , self )
. await
. map_err ( | err | {
error! ( " Unable to convert changes to edits: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
Ok ( code_action )
}
} else {
// The code action doesn't need to be resolved
Ok ( params )
} ;
2021-02-05 15:10:53 -05:00
self . performance . measure ( mark ) ;
result
}
2021-01-31 22:30:41 -05:00
async fn code_lens (
& mut self ,
params : CodeLensParams ,
) -> LspResult < Option < Vec < CodeLens > > > {
if ! self . enabled ( ) | | ! self . config . settings . enabled_code_lens ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " code_lens " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-01-31 22:30:41 -05:00
let line_index = self . get_line_index_sync ( & specifier ) . unwrap ( ) ;
let navigation_tree =
self . get_navigation_tree ( & specifier ) . await . map_err ( | err | {
2021-02-09 04:48:53 -05:00
error! ( " Failed to retrieve nav tree: {} " , err ) ;
2021-01-31 22:30:41 -05:00
LspError ::invalid_request ( )
} ) ? ;
// because we have to use this as a mutable in a closure, the compiler
// can't be sure when the vector will be mutated, and so a RefCell is
// required to "protect" the vector.
let cl = Rc ::new ( RefCell ::new ( Vec ::new ( ) ) ) ;
navigation_tree . walk ( & | i , mp | {
let mut code_lenses = cl . borrow_mut ( ) ;
2021-02-08 05:45:10 -05:00
// TSC Implementations Code Lens
2021-03-15 18:01:41 -04:00
if self . config . settings . code_lens . implementations {
2021-02-08 05:45:10 -05:00
let source = CodeLensSource ::Implementations ;
match i . kind {
tsc ::ScriptElementKind ::InterfaceElement = > {
code_lenses . push ( i . to_code_lens ( & line_index , & specifier , & source ) ) ;
}
tsc ::ScriptElementKind ::ClassElement
| tsc ::ScriptElementKind ::MemberFunctionElement
| tsc ::ScriptElementKind ::MemberVariableElement
| tsc ::ScriptElementKind ::MemberGetAccessorElement
| tsc ::ScriptElementKind ::MemberSetAccessorElement = > {
if ABSTRACT_MODIFIER . is_match ( & i . kind_modifiers ) {
code_lenses . push ( i . to_code_lens (
& line_index ,
& specifier ,
& source ,
) ) ;
}
}
_ = > ( ) ,
}
}
2021-01-31 22:30:41 -05:00
// TSC References Code Lens
2021-03-15 18:01:41 -04:00
if self . config . settings . code_lens . references {
2021-01-31 22:30:41 -05:00
let source = CodeLensSource ::References ;
if let Some ( parent ) = & mp {
if parent . kind = = tsc ::ScriptElementKind ::EnumElement {
code_lenses . push ( i . to_code_lens ( & line_index , & specifier , & source ) ) ;
}
}
match i . kind {
tsc ::ScriptElementKind ::FunctionElement = > {
2021-03-15 18:01:41 -04:00
if self . config . settings . code_lens . references_all_functions {
2021-01-31 22:30:41 -05:00
code_lenses . push ( i . to_code_lens (
& line_index ,
& specifier ,
& source ,
) ) ;
}
}
tsc ::ScriptElementKind ::ConstElement
| tsc ::ScriptElementKind ::LetElement
| tsc ::ScriptElementKind ::VariableElement = > {
if EXPORT_MODIFIER . is_match ( & i . kind_modifiers ) {
code_lenses . push ( i . to_code_lens (
& line_index ,
& specifier ,
& source ,
) ) ;
}
}
tsc ::ScriptElementKind ::ClassElement = > {
if i . text ! = " <class> " {
code_lenses . push ( i . to_code_lens (
& line_index ,
& specifier ,
& source ,
) ) ;
}
}
tsc ::ScriptElementKind ::InterfaceElement
| tsc ::ScriptElementKind ::TypeElement
| tsc ::ScriptElementKind ::EnumElement = > {
code_lenses . push ( i . to_code_lens ( & line_index , & specifier , & source ) ) ;
}
tsc ::ScriptElementKind ::LocalFunctionElement
| tsc ::ScriptElementKind ::MemberGetAccessorElement
| tsc ::ScriptElementKind ::MemberSetAccessorElement
| tsc ::ScriptElementKind ::ConstructorImplementationElement
| tsc ::ScriptElementKind ::MemberVariableElement = > {
if let Some ( parent ) = & mp {
if parent . spans [ 0 ] . start ! = i . spans [ 0 ] . start {
match parent . kind {
tsc ::ScriptElementKind ::ClassElement
| tsc ::ScriptElementKind ::InterfaceElement
| tsc ::ScriptElementKind ::TypeElement = > {
code_lenses . push ( i . to_code_lens (
& line_index ,
& specifier ,
& source ,
) ) ;
}
_ = > ( ) ,
}
}
}
}
_ = > ( ) ,
}
}
} ) ;
self . performance . measure ( mark ) ;
Ok ( Some ( Rc ::try_unwrap ( cl ) . unwrap ( ) . into_inner ( ) ) )
}
async fn code_lens_resolve (
& mut self ,
params : CodeLens ,
) -> LspResult < CodeLens > {
let mark = self . performance . mark ( " code_lens_resolve " ) ;
if let Some ( data ) = params . data . clone ( ) {
let code_lens_data : CodeLensData = serde_json ::from_value ( data )
. map_err ( | err | LspError ::invalid_params ( err . to_string ( ) ) ) ? ;
let code_lens = match code_lens_data . source {
2021-02-08 05:45:10 -05:00
CodeLensSource ::Implementations = > {
let line_index =
self . get_line_index_sync ( & code_lens_data . specifier ) . unwrap ( ) ;
let req = tsc ::RequestMethod ::GetImplementation ( (
code_lens_data . specifier . clone ( ) ,
line_index . offset_tsc ( params . range . start ) ? ,
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_implementations : Option < Vec < tsc ::ImplementationLocation > > =
2021-02-08 05:45:10 -05:00
self . ts_server . request ( self . snapshot ( ) , req ) . await . map_err (
| err | {
error! ( " Error processing TypeScript request: {} " , err ) ;
LspError ::internal_error ( )
} ,
) ? ;
if let Some ( implementations ) = maybe_implementations {
let mut locations = Vec ::new ( ) ;
for implementation in implementations {
2021-02-17 13:47:18 -05:00
let implementation_specifier = resolve_url (
2021-02-08 05:45:10 -05:00
& implementation . document_span . file_name ,
)
. map_err ( | err | {
error! ( " Invalid specifier returned from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
let implementation_location =
2021-02-17 23:37:05 -05:00
implementation . to_location ( & line_index , self ) ;
2021-02-08 05:45:10 -05:00
if ! ( implementation_specifier = = code_lens_data . specifier
& & implementation_location . range . start = = params . range . start )
{
locations . push ( implementation_location ) ;
}
}
let command = if ! locations . is_empty ( ) {
let title = if locations . len ( ) > 1 {
format! ( " {} implementations " , locations . len ( ) )
} else {
" 1 implementation " . to_string ( )
} ;
2021-02-17 23:37:05 -05:00
let url = self
. url_map
. normalize_specifier ( & code_lens_data . specifier )
2021-02-09 04:48:53 -05:00
. map_err ( | err | {
2021-02-17 23:37:05 -05:00
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-02-08 05:45:10 -05:00
Command {
title ,
command : " deno.showReferences " . to_string ( ) ,
arguments : Some ( vec! [
2021-02-09 04:48:53 -05:00
serde_json ::to_value ( url ) . unwrap ( ) ,
2021-02-08 05:45:10 -05:00
serde_json ::to_value ( params . range . start ) . unwrap ( ) ,
serde_json ::to_value ( locations ) . unwrap ( ) ,
] ) ,
}
} else {
Command {
title : " 0 implementations " . to_string ( ) ,
command : " " . to_string ( ) ,
arguments : None ,
}
} ;
CodeLens {
range : params . range ,
command : Some ( command ) ,
data : None ,
}
} else {
let command = Command {
title : " 0 implementations " . to_string ( ) ,
command : " " . to_string ( ) ,
arguments : None ,
} ;
CodeLens {
range : params . range ,
command : Some ( command ) ,
data : None ,
}
}
}
2021-01-31 22:30:41 -05:00
CodeLensSource ::References = > {
let line_index =
self . get_line_index_sync ( & code_lens_data . specifier ) . unwrap ( ) ;
let req = tsc ::RequestMethod ::GetReferences ( (
code_lens_data . specifier . clone ( ) ,
line_index . offset_tsc ( params . range . start ) ? ,
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_references : Option < Vec < tsc ::ReferenceEntry > > =
2021-01-31 22:30:41 -05:00
self . ts_server . request ( self . snapshot ( ) , req ) . await . map_err (
| err | {
error! ( " Error processing TypeScript request: {} " , err ) ;
LspError ::internal_error ( )
} ,
) ? ;
if let Some ( references ) = maybe_references {
let mut locations = Vec ::new ( ) ;
for reference in references {
if reference . is_definition {
continue ;
}
2021-02-17 13:47:18 -05:00
let reference_specifier = resolve_url (
2021-01-31 22:30:41 -05:00
& reference . document_span . file_name ,
)
. map_err ( | err | {
error! ( " Invalid specifier returned from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
let line_index = self
. get_line_index ( reference_specifier )
. await
. map_err ( | err | {
error! ( " Unable to get line index: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-02-17 23:37:05 -05:00
locations . push ( reference . to_location ( & line_index , self ) ) ;
2021-01-31 22:30:41 -05:00
}
let command = if ! locations . is_empty ( ) {
let title = if locations . len ( ) > 1 {
format! ( " {} references " , locations . len ( ) )
} else {
" 1 reference " . to_string ( )
} ;
2021-02-17 23:37:05 -05:00
let url = self
. url_map
. normalize_specifier ( & code_lens_data . specifier )
2021-02-09 04:48:53 -05:00
. map_err ( | err | {
2021-02-17 23:37:05 -05:00
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-01-31 22:30:41 -05:00
Command {
title ,
command : " deno.showReferences " . to_string ( ) ,
arguments : Some ( vec! [
2021-02-09 04:48:53 -05:00
serde_json ::to_value ( url ) . unwrap ( ) ,
2021-01-31 22:30:41 -05:00
serde_json ::to_value ( params . range . start ) . unwrap ( ) ,
serde_json ::to_value ( locations ) . unwrap ( ) ,
] ) ,
}
} else {
Command {
title : " 0 references " . to_string ( ) ,
command : " " . to_string ( ) ,
arguments : None ,
}
} ;
CodeLens {
range : params . range ,
command : Some ( command ) ,
data : None ,
}
} else {
let command = Command {
title : " 0 references " . to_string ( ) ,
command : " " . to_string ( ) ,
arguments : None ,
} ;
CodeLens {
range : params . range ,
command : Some ( command ) ,
data : None ,
}
}
}
} ;
self . performance . measure ( mark ) ;
Ok ( code_lens )
} else {
self . performance . measure ( mark ) ;
Err ( LspError ::invalid_params (
" Code lens is missing the \" data \" property. " ,
) )
}
}
2020-12-21 08:44:26 -05:00
async fn document_highlight (
2021-02-11 23:17:48 -05:00
& self ,
2020-12-21 08:44:26 -05:00
params : DocumentHighlightParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < Vec < DocumentHighlight > > > {
2020-12-22 00:42:32 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " document_highlight " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
2021-01-22 05:03:16 -05:00
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2020-12-21 08:44:26 -05:00
let files_to_search = vec! [ specifier . clone ( ) ] ;
let req = tsc ::RequestMethod ::GetDocumentHighlights ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
2020-12-21 08:44:26 -05:00
files_to_search ,
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_document_highlights : Option < Vec < tsc ::DocumentHighlights > > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get document highlights from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-21 08:44:26 -05:00
if let Some ( document_highlights ) = maybe_document_highlights {
2021-01-26 19:32:49 -05:00
let result = document_highlights
. into_iter ( )
. map ( | dh | dh . to_highlight ( & line_index ) )
. flatten ( )
. collect ( ) ;
self . performance . measure ( mark ) ;
Ok ( Some ( result ) )
2020-12-21 08:44:26 -05:00
} else {
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( None )
}
}
async fn references (
2021-01-26 04:55:04 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
params : ReferenceParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < Vec < Location > > > {
2020-12-22 00:42:32 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " references " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position . text_document . uri ) ;
2021-01-22 05:03:16 -05:00
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2020-12-21 08:44:26 -05:00
let req = tsc ::RequestMethod ::GetReferences ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position . position ) ? ,
2020-12-21 08:44:26 -05:00
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_references : Option < Vec < tsc ::ReferenceEntry > > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get references from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-21 08:44:26 -05:00
if let Some ( references ) = maybe_references {
let mut results = Vec ::new ( ) ;
for reference in references {
if ! params . context . include_declaration & & reference . is_definition {
continue ;
}
let reference_specifier =
2021-02-17 13:47:18 -05:00
resolve_url ( & reference . document_span . file_name ) . unwrap ( ) ;
2020-12-21 08:44:26 -05:00
// TODO(lucacasonato): handle error correctly
let line_index =
self . get_line_index ( reference_specifier ) . await . unwrap ( ) ;
2021-02-17 23:37:05 -05:00
results . push ( reference . to_location ( & line_index , self ) ) ;
2020-12-21 08:44:26 -05:00
}
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( Some ( results ) )
} else {
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( None )
}
}
async fn goto_definition (
2021-01-26 04:55:04 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
params : GotoDefinitionParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < GotoDefinitionResponse > > {
2020-12-22 00:42:32 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " goto_definition " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
2021-01-22 05:03:16 -05:00
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2020-12-21 08:44:26 -05:00
let req = tsc ::RequestMethod ::GetDefinition ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
2020-12-21 08:44:26 -05:00
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_definition : Option < tsc ::DefinitionInfoAndBoundSpan > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get definition from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-21 08:44:26 -05:00
if let Some ( definition ) = maybe_definition {
2021-02-06 07:39:01 -05:00
let results = definition . to_definition ( & line_index , self ) . await ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
Ok ( results )
2020-12-21 08:44:26 -05:00
} else {
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( None )
}
}
async fn completion (
2021-02-11 23:17:48 -05:00
& self ,
2020-12-21 08:44:26 -05:00
params : CompletionParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < CompletionResponse > > {
2020-12-22 00:42:32 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " completion " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position . text_document . uri ) ;
2021-03-24 20:13:37 -04:00
// Import specifiers are something wholly internal to Deno, so for
// completions, we will use internal logic and if there are completions
// for imports, we will return those and not send a message into tsc, where
// other completions come from.
let response = if let Some ( response ) = completions ::get_import_completions (
& specifier ,
& params . text_document_position . position ,
& self . snapshot ( ) ,
2021-04-08 21:27:27 -04:00
)
. await
{
2021-03-24 20:13:37 -04:00
Some ( response )
} else {
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let trigger_character = if let Some ( context ) = & params . context {
context . trigger_character . clone ( )
2021-01-22 05:03:16 -05:00
} else {
2021-03-24 20:13:37 -04:00
None
2021-01-22 05:03:16 -05:00
} ;
2021-03-24 20:13:37 -04:00
let position =
line_index . offset_tsc ( params . text_document_position . position ) ? ;
let req = tsc ::RequestMethod ::GetCompletions ( (
specifier . clone ( ) ,
position ,
tsc ::GetCompletionsAtPositionOptions {
user_preferences : tsc ::UserPreferences {
include_completions_with_insert_text : Some ( true ) ,
.. Default ::default ( )
} ,
trigger_character ,
2021-03-15 18:01:41 -04:00
} ,
2021-03-24 20:13:37 -04:00
) ) ;
let maybe_completion_info : Option < tsc ::CompletionInfo > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Unable to get completion info from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-21 08:44:26 -05:00
2021-03-24 20:13:37 -04:00
if let Some ( completions ) = maybe_completion_info {
let results = completions . as_completion_response (
& line_index ,
& self . config . settings . suggest ,
& specifier ,
position ,
) ;
Some ( results )
} else {
None
}
} ;
self . performance . measure ( mark ) ;
Ok ( response )
2020-12-21 08:44:26 -05:00
}
2021-03-15 18:01:41 -04:00
async fn completion_resolve (
& mut self ,
params : CompletionItem ,
) -> LspResult < CompletionItem > {
let mark = self . performance . mark ( " completion_resolve " ) ;
2021-03-24 20:13:37 -04:00
let completion_item = if let Some ( data ) = & params . data {
let data : completions ::CompletionItemData =
serde_json ::from_value ( data . clone ( ) ) . map_err ( | err | {
2021-03-15 18:01:41 -04:00
error! ( " {} " , err ) ;
LspError ::invalid_params (
" Could not decode data field of completion item. " ,
)
} ) ? ;
2021-03-24 20:13:37 -04:00
if let Some ( data ) = data . tsc {
let req = tsc ::RequestMethod ::GetCompletionDetails ( data . into ( ) ) ;
let maybe_completion_info : Option < tsc ::CompletionEntryDetails > =
self . ts_server . request ( self . snapshot ( ) , req ) . await . map_err (
| err | {
error! ( " Unable to get completion info from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} ,
) ? ;
if let Some ( completion_info ) = maybe_completion_info {
completion_info . as_completion_item ( & params )
} else {
error! (
" Received an undefined response from tsc for completion details. "
) ;
params
}
2021-03-15 18:01:41 -04:00
} else {
2021-03-24 20:13:37 -04:00
params
2021-03-15 18:01:41 -04:00
}
} else {
2021-03-24 20:13:37 -04:00
params
} ;
self . performance . measure ( mark ) ;
Ok ( completion_item )
2021-03-15 18:01:41 -04:00
}
2021-01-12 16:53:27 -05:00
async fn goto_implementation (
2021-02-06 07:39:01 -05:00
& mut self ,
2021-01-12 16:53:27 -05:00
params : GotoImplementationParams ,
) -> LspResult < Option < GotoImplementationResponse > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " goto_implementation " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
2021-01-12 16:53:27 -05:00
let line_index =
2021-01-22 05:03:16 -05:00
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2021-01-12 16:53:27 -05:00
let req = tsc ::RequestMethod ::GetImplementation ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
2021-01-12 16:53:27 -05:00
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_implementations : Option < Vec < tsc ::ImplementationLocation > > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
2021-01-12 16:53:27 -05:00
. map_err ( | err | {
2021-02-24 22:15:55 -05:00
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
2021-01-12 16:53:27 -05:00
} ) ? ;
2021-02-17 22:15:13 -05:00
let result = if let Some ( implementations ) = maybe_implementations {
let mut links = Vec ::new ( ) ;
for implementation in implementations {
if let Some ( link ) = implementation . to_link ( & line_index , self ) . await {
links . push ( link )
2021-01-12 16:53:27 -05:00
}
}
2021-02-17 22:15:13 -05:00
Some ( GotoDefinitionResponse ::Link ( links ) )
2021-01-12 16:53:27 -05:00
} else {
2021-02-17 22:15:13 -05:00
None
} ;
self . performance . measure ( mark ) ;
Ok ( result )
2021-01-12 16:53:27 -05:00
}
2021-04-02 02:21:07 -04:00
async fn folding_range (
& self ,
params : FoldingRangeParams ,
) -> LspResult < Option < Vec < FoldingRange > > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " folding_range " ) ;
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::GetOutliningSpans ( specifier . clone ( ) ) ;
let outlining_spans : Vec < tsc ::OutliningSpan > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let response = if ! outlining_spans . is_empty ( ) {
let text_content =
self . get_text_content ( & specifier ) . ok_or_else ( | | {
LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) )
} ) ? ;
Some (
outlining_spans
. iter ( )
. map ( | span | {
span . to_folding_range (
& line_index ,
text_content . as_str ( ) . as_bytes ( ) ,
self . config . client_capabilities . line_folding_only ,
)
} )
. collect ::< Vec < FoldingRange > > ( ) ,
)
} else {
None
} ;
self . performance . measure ( mark ) ;
Ok ( response )
}
2021-04-19 01:11:26 -04:00
async fn incoming_calls (
& mut self ,
params : CallHierarchyIncomingCallsParams ,
) -> LspResult < Option < Vec < CallHierarchyIncomingCall > > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " incoming_calls " ) ;
let specifier = self . url_map . normalize_url ( & params . item . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::ProvideCallHierarchyIncomingCalls ( (
specifier . clone ( ) ,
line_index . offset_tsc ( params . item . selection_range . start ) ? ,
) ) ;
let incoming_calls : Vec < tsc ::CallHierarchyIncomingCall > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let maybe_root_path_owned = self
. config
. root_uri
. as_ref ( )
. and_then ( | uri | uri . to_file_path ( ) . ok ( ) ) ;
let mut resolved_items = Vec ::< CallHierarchyIncomingCall > ::new ( ) ;
for item in incoming_calls . iter ( ) {
if let Some ( resolved ) = item
. try_resolve_call_hierarchy_incoming_call (
self ,
maybe_root_path_owned . as_deref ( ) ,
)
. await
{
resolved_items . push ( resolved ) ;
}
}
self . performance . measure ( mark ) ;
Ok ( Some ( resolved_items ) )
}
async fn outgoing_calls (
& mut self ,
params : CallHierarchyOutgoingCallsParams ,
) -> LspResult < Option < Vec < CallHierarchyOutgoingCall > > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " outgoing_calls " ) ;
let specifier = self . url_map . normalize_url ( & params . item . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::ProvideCallHierarchyOutgoingCalls ( (
specifier . clone ( ) ,
line_index . offset_tsc ( params . item . selection_range . start ) ? ,
) ) ;
let outgoing_calls : Vec < tsc ::CallHierarchyOutgoingCall > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let maybe_root_path_owned = self
. config
. root_uri
. as_ref ( )
. and_then ( | uri | uri . to_file_path ( ) . ok ( ) ) ;
let mut resolved_items = Vec ::< CallHierarchyOutgoingCall > ::new ( ) ;
for item in outgoing_calls . iter ( ) {
if let Some ( resolved ) = item
. try_resolve_call_hierarchy_outgoing_call (
& line_index ,
self ,
maybe_root_path_owned . as_deref ( ) ,
)
. await
{
resolved_items . push ( resolved ) ;
}
}
self . performance . measure ( mark ) ;
Ok ( Some ( resolved_items ) )
}
async fn prepare_call_hierarchy (
& mut self ,
params : CallHierarchyPrepareParams ,
) -> LspResult < Option < Vec < CallHierarchyItem > > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " prepare_call_hierarchy " ) ;
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::PrepareCallHierarchy ( (
specifier . clone ( ) ,
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
) ) ;
let maybe_one_or_many : Option < tsc ::OneOrMany < tsc ::CallHierarchyItem > > =
self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let response = if let Some ( one_or_many ) = maybe_one_or_many {
let maybe_root_path_owned = self
. config
. root_uri
. as_ref ( )
. and_then ( | uri | uri . to_file_path ( ) . ok ( ) ) ;
let mut resolved_items = Vec ::< CallHierarchyItem > ::new ( ) ;
match one_or_many {
tsc ::OneOrMany ::One ( item ) = > {
if let Some ( resolved ) = item
. try_resolve_call_hierarchy_item (
self ,
maybe_root_path_owned . as_deref ( ) ,
)
. await
{
resolved_items . push ( resolved )
}
}
tsc ::OneOrMany ::Many ( items ) = > {
for item in items . iter ( ) {
if let Some ( resolved ) = item
. try_resolve_call_hierarchy_item (
self ,
maybe_root_path_owned . as_deref ( ) ,
)
. await
{
resolved_items . push ( resolved ) ;
}
}
}
}
Some ( resolved_items )
} else {
None
} ;
self . performance . measure ( mark ) ;
Ok ( response )
}
2020-12-29 19:58:20 -05:00
async fn rename (
2021-02-06 07:39:01 -05:00
& mut self ,
2020-12-29 19:58:20 -05:00
params : RenameParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < WorkspaceEdit > > {
2020-12-29 19:58:20 -05:00
if ! self . enabled ( ) {
return Ok ( None ) ;
}
2021-02-06 07:39:01 -05:00
let mark = self . performance . mark ( " rename " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position . text_document . uri ) ;
2020-12-29 19:58:20 -05:00
let line_index =
2021-01-22 05:03:16 -05:00
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
2020-12-29 19:58:20 -05:00
let req = tsc ::RequestMethod ::FindRenameLocations ( (
specifier ,
2021-01-22 05:03:16 -05:00
line_index . offset_tsc ( params . text_document_position . position ) ? ,
2020-12-29 19:58:20 -05:00
true ,
true ,
false ,
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_locations : Option < Vec < tsc ::RenameLocation > > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
2020-12-29 19:58:20 -05:00
2021-01-22 05:03:16 -05:00
if let Some ( locations ) = maybe_locations {
let rename_locations = tsc ::RenameLocations { locations } ;
let workspace_edits = rename_locations
2021-02-06 07:39:01 -05:00
. into_workspace_edit ( & params . new_name , self )
2021-01-22 05:03:16 -05:00
. await
. map_err ( | err | {
2021-02-09 04:48:53 -05:00
error! ( " Failed to get workspace edits: {} " , err ) ;
2021-01-22 05:03:16 -05:00
LspError ::internal_error ( )
} ) ? ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-01-22 05:03:16 -05:00
Ok ( Some ( workspace_edits ) )
} else {
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-01-22 05:03:16 -05:00
Ok ( None )
2020-12-29 19:58:20 -05:00
}
}
2020-12-21 08:44:26 -05:00
async fn request_else (
2021-01-26 04:55:04 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
method : & str ,
params : Option < Value > ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < Value > > {
2020-12-21 08:44:26 -05:00
match method {
2020-12-29 23:17:17 -05:00
" deno/cache " = > match params . map ( serde_json ::from_value ) {
2021-04-08 21:27:27 -04:00
Some ( Ok ( params ) ) = > self . cache ( params ) . await ,
2020-12-29 23:17:17 -05:00
Some ( Err ( err ) ) = > Err ( LspError ::invalid_params ( err . to_string ( ) ) ) ,
None = > Err ( LspError ::invalid_params ( " Missing parameters " ) ) ,
} ,
2021-02-12 05:08:36 -05:00
" deno/performance " = > Ok ( Some ( self . get_performance ( ) ) ) ,
2021-04-08 21:27:27 -04:00
" deno/reloadImportRegistries " = > self . reload_import_registries ( ) . await ,
2020-12-21 08:44:26 -05:00
" deno/virtualTextDocument " = > match params . map ( serde_json ::from_value ) {
Some ( Ok ( params ) ) = > Ok ( Some (
serde_json ::to_value ( self . virtual_text_document ( params ) . await ? )
. map_err ( | err | {
error! (
2021-02-09 04:48:53 -05:00
" Failed to serialize virtual_text_document response: {} " ,
2020-12-21 08:44:26 -05:00
err
) ;
2020-12-29 23:17:17 -05:00
LspError ::internal_error ( )
2020-12-21 08:44:26 -05:00
} ) ? ,
) ) ,
2020-12-29 23:17:17 -05:00
Some ( Err ( err ) ) = > Err ( LspError ::invalid_params ( err . to_string ( ) ) ) ,
None = > Err ( LspError ::invalid_params ( " Missing parameters " ) ) ,
2020-12-21 08:44:26 -05:00
} ,
_ = > {
error! ( " Got a {} request, but no handler is defined " , method ) ;
2020-12-29 23:17:17 -05:00
Err ( LspError ::method_not_found ( ) )
2020-12-21 08:44:26 -05:00
}
}
}
2021-02-15 21:34:09 -05:00
2021-03-23 19:33:25 -04:00
async fn selection_range (
& self ,
params : SelectionRangeParams ,
) -> LspResult < Option < Vec < SelectionRange > > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " selection_range " ) ;
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let mut selection_ranges = Vec ::< SelectionRange > ::new ( ) ;
for position in params . positions {
let req = tsc ::RequestMethod ::GetSmartSelectionRange ( (
specifier . clone ( ) ,
line_index . offset_tsc ( position ) ? ,
) ) ;
let selection_range : tsc ::SelectionRange = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
selection_ranges . push ( selection_range . to_selection_range ( & line_index ) ) ;
}
self . performance . measure ( mark ) ;
Ok ( Some ( selection_ranges ) )
}
2021-04-19 21:26:36 -04:00
async fn semantic_tokens_full (
& self ,
params : SemanticTokensParams ,
) -> LspResult < Option < SemanticTokensResult > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " semantic_tokens_full " ) ;
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let req = tsc ::RequestMethod ::GetEncodedSemanticClassifications ( (
specifier . clone ( ) ,
tsc ::TextSpan {
start : 0 ,
length : line_index . text_content_length_utf16 ( ) . into ( ) ,
} ,
) ) ;
let semantic_classification : tsc ::Classifications = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let semantic_tokens : SemanticTokens =
semantic_classification . to_semantic_tokens ( & line_index ) ;
let response = if ! semantic_tokens . data . is_empty ( ) {
Some ( SemanticTokensResult ::Tokens ( semantic_tokens ) )
} else {
None
} ;
self . performance . measure ( mark ) ;
Ok ( response )
}
async fn semantic_tokens_range (
& self ,
params : SemanticTokensRangeParams ,
) -> LspResult < Option < SemanticTokensRangeResult > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " semantic_tokens_range " ) ;
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let start = line_index . offset_tsc ( params . range . start ) ? ;
let length = line_index . offset_tsc ( params . range . end ) ? - start ;
let req = tsc ::RequestMethod ::GetEncodedSemanticClassifications ( (
specifier . clone ( ) ,
tsc ::TextSpan { start , length } ,
) ) ;
let semantic_classification : tsc ::Classifications = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} ) ? ;
let semantic_tokens : SemanticTokens =
semantic_classification . to_semantic_tokens ( & line_index ) ;
let response = if ! semantic_tokens . data . is_empty ( ) {
Some ( SemanticTokensRangeResult ::Tokens ( semantic_tokens ) )
} else {
None
} ;
self . performance . measure ( mark ) ;
Ok ( response )
}
2021-02-15 21:34:09 -05:00
async fn signature_help (
& self ,
params : SignatureHelpParams ,
) -> LspResult < Option < SignatureHelp > > {
if ! self . enabled ( ) {
return Ok ( None ) ;
}
let mark = self . performance . mark ( " signature_help " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self
. url_map
. normalize_url ( & params . text_document_position_params . text_document . uri ) ;
2021-02-15 21:34:09 -05:00
let line_index =
if let Some ( line_index ) = self . get_line_index_sync ( & specifier ) {
line_index
} else {
return Err ( LspError ::invalid_params ( format! (
" An unexpected specifier ({}) was provided. " ,
specifier
) ) ) ;
} ;
let options = if let Some ( context ) = params . context {
tsc ::SignatureHelpItemsOptions {
trigger_reason : Some ( tsc ::SignatureHelpTriggerReason {
kind : context . trigger_kind . into ( ) ,
trigger_character : context . trigger_character ,
} ) ,
}
} else {
tsc ::SignatureHelpItemsOptions {
trigger_reason : None ,
}
} ;
let req = tsc ::RequestMethod ::GetSignatureHelpItems ( (
specifier ,
line_index . offset_tsc ( params . text_document_position_params . position ) ? ,
options ,
) ) ;
2021-02-24 22:15:55 -05:00
let maybe_signature_help_items : Option < tsc ::SignatureHelpItems > = self
. ts_server
. request ( self . snapshot ( ) , req )
. await
. map_err ( | err | {
error! ( " Failed to request to tsserver: {} " , err ) ;
LspError ::invalid_request ( )
2021-02-15 21:34:09 -05:00
} ) ? ;
if let Some ( signature_help_items ) = maybe_signature_help_items {
let signature_help = signature_help_items . into_signature_help ( ) ;
self . performance . measure ( mark ) ;
Ok ( Some ( signature_help ) )
} else {
self . performance . measure ( mark ) ;
Ok ( None )
}
}
2020-12-21 08:44:26 -05:00
}
2021-01-26 04:55:04 -05:00
#[ lspower::async_trait ]
impl lspower ::LanguageServer for LanguageServer {
async fn initialize (
& self ,
params : InitializeParams ,
) -> LspResult < InitializeResult > {
2021-03-09 21:41:35 -05:00
let mut language_server = self . 0. lock ( ) . await ;
let client = language_server . client . clone ( ) ;
let ts_server = language_server . ts_server . clone ( ) ;
language_server
. diagnostics_server
. start ( self . 0. clone ( ) , client , ts_server ) ;
language_server . initialize ( params ) . await
2021-01-26 04:55:04 -05:00
}
async fn initialized ( & self , params : InitializedParams ) {
self . 0. lock ( ) . await . initialized ( params ) . await
}
async fn shutdown ( & self ) -> LspResult < ( ) > {
self . 0. lock ( ) . await . shutdown ( ) . await
}
async fn did_open ( & self , params : DidOpenTextDocumentParams ) {
self . 0. lock ( ) . await . did_open ( params ) . await
}
async fn did_change ( & self , params : DidChangeTextDocumentParams ) {
self . 0. lock ( ) . await . did_change ( params ) . await
}
2021-02-09 17:46:12 -05:00
async fn did_save ( & self , _params : DidSaveTextDocumentParams ) {
// We don't need to do anything on save at the moment, but if this isn't
// implemented, lspower complains about it not being implemented.
}
2021-01-26 04:55:04 -05:00
async fn did_close ( & self , params : DidCloseTextDocumentParams ) {
self . 0. lock ( ) . await . did_close ( params ) . await
}
async fn did_change_configuration (
& self ,
params : DidChangeConfigurationParams ,
) {
self . 0. lock ( ) . await . did_change_configuration ( params ) . await
}
async fn did_change_watched_files (
& self ,
params : DidChangeWatchedFilesParams ,
) {
self . 0. lock ( ) . await . did_change_watched_files ( params ) . await
}
2021-04-19 21:29:27 -04:00
async fn document_symbol (
& self ,
params : DocumentSymbolParams ,
) -> LspResult < Option < DocumentSymbolResponse > > {
self . 0. lock ( ) . await . document_symbol ( params ) . await
}
2021-01-26 04:55:04 -05:00
async fn formatting (
& self ,
params : DocumentFormattingParams ,
) -> LspResult < Option < Vec < TextEdit > > > {
self . 0. lock ( ) . await . formatting ( params ) . await
}
async fn hover ( & self , params : HoverParams ) -> LspResult < Option < Hover > > {
self . 0. lock ( ) . await . hover ( params ) . await
}
2021-02-04 13:53:02 -05:00
async fn code_action (
& self ,
params : CodeActionParams ,
) -> LspResult < Option < CodeActionResponse > > {
self . 0. lock ( ) . await . code_action ( params ) . await
}
2021-02-05 15:10:53 -05:00
async fn code_action_resolve (
& self ,
params : CodeAction ,
) -> LspResult < CodeAction > {
self . 0. lock ( ) . await . code_action_resolve ( params ) . await
}
2021-01-31 22:30:41 -05:00
async fn code_lens (
& self ,
params : CodeLensParams ,
) -> LspResult < Option < Vec < CodeLens > > > {
self . 0. lock ( ) . await . code_lens ( params ) . await
}
async fn code_lens_resolve ( & self , params : CodeLens ) -> LspResult < CodeLens > {
self . 0. lock ( ) . await . code_lens_resolve ( params ) . await
}
2021-01-26 04:55:04 -05:00
async fn document_highlight (
& self ,
params : DocumentHighlightParams ,
) -> LspResult < Option < Vec < DocumentHighlight > > > {
self . 0. lock ( ) . await . document_highlight ( params ) . await
}
async fn references (
& self ,
params : ReferenceParams ,
) -> LspResult < Option < Vec < Location > > > {
self . 0. lock ( ) . await . references ( params ) . await
}
async fn goto_definition (
& self ,
params : GotoDefinitionParams ,
) -> LspResult < Option < GotoDefinitionResponse > > {
self . 0. lock ( ) . await . goto_definition ( params ) . await
}
async fn completion (
& self ,
params : CompletionParams ,
) -> LspResult < Option < CompletionResponse > > {
self . 0. lock ( ) . await . completion ( params ) . await
}
2021-03-15 18:01:41 -04:00
async fn completion_resolve (
& self ,
params : CompletionItem ,
) -> LspResult < CompletionItem > {
self . 0. lock ( ) . await . completion_resolve ( params ) . await
}
2021-01-26 04:55:04 -05:00
async fn goto_implementation (
& self ,
params : GotoImplementationParams ,
) -> LspResult < Option < GotoImplementationResponse > > {
self . 0. lock ( ) . await . goto_implementation ( params ) . await
}
2021-04-02 02:21:07 -04:00
async fn folding_range (
& self ,
params : FoldingRangeParams ,
) -> LspResult < Option < Vec < FoldingRange > > > {
self . 0. lock ( ) . await . folding_range ( params ) . await
}
2021-04-19 01:11:26 -04:00
async fn incoming_calls (
& self ,
params : CallHierarchyIncomingCallsParams ,
) -> LspResult < Option < Vec < CallHierarchyIncomingCall > > > {
self . 0. lock ( ) . await . incoming_calls ( params ) . await
}
async fn outgoing_calls (
& self ,
params : CallHierarchyOutgoingCallsParams ,
) -> LspResult < Option < Vec < CallHierarchyOutgoingCall > > > {
self . 0. lock ( ) . await . outgoing_calls ( params ) . await
}
async fn prepare_call_hierarchy (
& self ,
params : CallHierarchyPrepareParams ,
) -> LspResult < Option < Vec < CallHierarchyItem > > > {
self . 0. lock ( ) . await . prepare_call_hierarchy ( params ) . await
}
2021-01-26 04:55:04 -05:00
async fn rename (
& self ,
params : RenameParams ,
) -> LspResult < Option < WorkspaceEdit > > {
self . 0. lock ( ) . await . rename ( params ) . await
}
async fn request_else (
& self ,
method : & str ,
params : Option < Value > ,
) -> LspResult < Option < Value > > {
self . 0. lock ( ) . await . request_else ( method , params ) . await
}
2021-02-15 21:34:09 -05:00
2021-03-23 19:33:25 -04:00
async fn selection_range (
& self ,
params : SelectionRangeParams ,
) -> LspResult < Option < Vec < SelectionRange > > > {
self . 0. lock ( ) . await . selection_range ( params ) . await
}
2021-04-19 21:26:36 -04:00
async fn semantic_tokens_full (
& self ,
params : SemanticTokensParams ,
) -> LspResult < Option < SemanticTokensResult > > {
self . 0. lock ( ) . await . semantic_tokens_full ( params ) . await
}
async fn semantic_tokens_range (
& self ,
params : SemanticTokensRangeParams ,
) -> LspResult < Option < SemanticTokensRangeResult > > {
self . 0. lock ( ) . await . semantic_tokens_range ( params ) . await
}
2021-02-15 21:34:09 -05:00
async fn signature_help (
& self ,
params : SignatureHelpParams ,
) -> LspResult < Option < SignatureHelp > > {
self . 0. lock ( ) . await . signature_help ( params ) . await
}
2021-01-26 04:55:04 -05:00
}
2020-12-29 23:17:17 -05:00
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
2021-01-26 04:55:04 -05:00
struct CacheParams {
2021-02-11 23:17:48 -05:00
/// The document currently open in the editor. If there are no `uris`
/// supplied, the referrer will be cached.
referrer : TextDocumentIdentifier ,
/// Any documents that have been specifically asked to be cached via the
/// command.
uris : Vec < TextDocumentIdentifier > ,
2020-12-29 23:17:17 -05:00
}
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
2021-01-26 04:55:04 -05:00
struct VirtualTextDocumentParams {
text_document : TextDocumentIdentifier ,
2020-12-29 23:17:17 -05:00
}
2021-02-04 13:53:02 -05:00
// These are implementations of custom commands supported by the LSP
2021-01-26 04:55:04 -05:00
impl Inner {
2021-02-11 23:17:48 -05:00
/// Similar to `deno cache` on the command line, where modules will be cached
/// in the Deno cache, including any of their dependencies.
2021-04-08 21:27:27 -04:00
async fn cache ( & mut self , params : CacheParams ) -> LspResult < Option < Value > > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " cache " ) ;
2021-02-17 23:37:05 -05:00
let referrer = self . url_map . normalize_url ( & params . referrer . uri ) ;
2021-02-11 23:17:48 -05:00
if ! params . uris . is_empty ( ) {
for identifier in & params . uris {
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & identifier . uri ) ;
2021-02-11 23:17:48 -05:00
sources ::cache ( & specifier , & self . maybe_import_map )
. await
. map_err ( | err | {
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
}
} else {
sources ::cache ( & referrer , & self . maybe_import_map )
. await
. map_err ( | err | {
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2020-12-29 23:17:17 -05:00
}
2021-02-11 23:17:48 -05:00
// now that we have dependencies loaded, we need to re-analyze them and
// invalidate some diagnostics
2021-02-12 06:49:42 -05:00
if self . documents . contains_key ( & referrer ) {
2021-02-11 23:17:48 -05:00
if let Some ( source ) = self . documents . content ( & referrer ) . unwrap ( ) {
self . analyze_dependencies ( & referrer , & source ) ;
}
2021-03-09 21:41:35 -05:00
self
. diagnostics_server
. invalidate ( referrer )
. map_err ( | err | {
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-02-11 23:17:48 -05:00
}
2021-03-09 21:41:35 -05:00
self . diagnostics_server . update ( ) . map_err ( | err | {
2020-12-29 23:17:17 -05:00
error! ( " {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2021-04-08 21:27:27 -04:00
Ok ( Some ( json! ( true ) ) )
2020-12-29 23:17:17 -05:00
}
2021-02-12 05:08:36 -05:00
fn get_performance ( & self ) -> Value {
2021-01-26 19:32:49 -05:00
let averages = self . performance . averages ( ) ;
2021-02-12 05:08:36 -05:00
json! ( { " averages " : averages } )
2021-01-26 19:32:49 -05:00
}
2021-04-08 21:27:27 -04:00
async fn reload_import_registries ( & mut self ) -> LspResult < Option < Value > > {
fs ::remove_dir_all ( & self . module_registries_location )
. await
. map_err ( | err | {
error! ( " Unable to remove registries cache: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
self . module_registries =
registries ::ModuleRegistry ::new ( & self . module_registries_location ) ;
self . update_registries ( ) . await . map_err ( | err | {
error! ( " Unable to update registries: {} " , err ) ;
LspError ::internal_error ( )
} ) ? ;
Ok ( Some ( json! ( true ) ) )
}
2020-12-21 08:44:26 -05:00
async fn virtual_text_document (
2021-02-06 07:39:01 -05:00
& mut self ,
2020-12-21 08:44:26 -05:00
params : VirtualTextDocumentParams ,
2020-12-29 23:17:17 -05:00
) -> LspResult < Option < String > > {
2021-01-26 19:32:49 -05:00
let mark = self . performance . mark ( " virtual_text_document " ) ;
2021-02-17 23:37:05 -05:00
let specifier = self . url_map . normalize_url ( & params . text_document . uri ) ;
2021-02-17 13:47:18 -05:00
let contents = if specifier . as_str ( ) = = " deno:/status.md " {
2021-01-26 19:32:49 -05:00
let mut contents = String ::new ( ) ;
2021-04-19 17:10:43 -04:00
let mut documents_specifiers = self . documents . specifiers ( ) ;
documents_specifiers . sort ( ) ;
let mut sources_specifiers = self . sources . specifiers ( ) ;
sources_specifiers . sort ( ) ;
let measures = self . performance . to_vec ( ) ;
2021-01-26 19:32:49 -05:00
contents . push_str ( & format! (
2020-12-21 08:44:26 -05:00
r #" # Deno Language Server Status
2021-01-04 16:52:20 -05:00
2021-04-19 17:10:43 -04:00
- < details > < summary > Documents in memory : { } < / summary >
- { }
< / details >
- < details > < summary > Sources in memory : { } < / summary >
- { }
2021-04-20 22:41:24 -04:00
2021-04-19 17:10:43 -04:00
< / details >
- < details > < summary > Performance measures : { } < / summary >
- { }
< / details >
2021-01-26 19:32:49 -05:00
" #,
2021-04-19 17:10:43 -04:00
self . documents . len ( ) ,
documents_specifiers
. into_iter ( )
. map ( | s | s . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " \n - " ) ,
self . sources . len ( ) ,
sources_specifiers
. into_iter ( )
. map ( | s | s . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " \n - " ) ,
measures . len ( ) ,
measures
. iter ( )
. map ( | m | m . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " \n - " )
2021-01-26 19:32:49 -05:00
) ) ;
2021-04-19 17:10:43 -04:00
contents
. push_str ( " \n ## Performance \n \n |Name|Duration|Count| \n |---|---|---| \n " ) ;
let mut averages = self . performance . averages ( ) ;
averages . sort ( ) ;
for average in averages {
2021-01-26 19:32:49 -05:00
contents . push_str ( & format! (
2021-04-19 17:10:43 -04:00
" |{}|{}ms|{}| \n " ,
2021-01-26 19:32:49 -05:00
average . name , average . average_duration , average . count
) ) ;
}
Some ( contents )
2020-12-21 08:44:26 -05:00
} else {
2021-02-17 13:47:18 -05:00
match specifier . scheme ( ) {
2020-12-21 08:44:26 -05:00
" asset " = > {
2021-02-06 07:39:01 -05:00
if let Some ( asset ) = self
. get_asset ( & specifier )
. await
. map_err ( | _ | LspError ::internal_error ( ) ) ?
{
Some ( asset . text )
2020-12-21 08:44:26 -05:00
} else {
2021-02-06 07:39:01 -05:00
error! ( " Missing asset: {} " , specifier ) ;
None
2020-12-21 08:44:26 -05:00
}
}
_ = > {
2021-02-15 04:32:06 -05:00
if let Some ( source ) = self . sources . get_source ( & specifier ) {
Some ( source )
2020-12-21 08:44:26 -05:00
} else {
2021-04-06 07:45:53 -04:00
error! ( " The cached source was not found: {} " , specifier ) ;
2020-12-21 08:44:26 -05:00
None
}
}
}
} ;
2021-01-26 19:32:49 -05:00
self . performance . measure ( mark ) ;
2020-12-21 08:44:26 -05:00
Ok ( contents )
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2021-01-26 19:32:49 -05:00
use crate ::lsp ::performance ::PerformanceAverage ;
2020-12-21 08:44:26 -05:00
use lspower ::jsonrpc ;
use lspower ::ExitedError ;
use lspower ::LspService ;
use std ::fs ;
use std ::task ::Poll ;
2021-01-22 05:03:16 -05:00
use std ::time ::Instant ;
2021-04-19 17:11:23 -04:00
use tempfile ::TempDir ;
2020-12-21 08:44:26 -05:00
use tower_test ::mock ::Spawn ;
2021-01-26 19:32:49 -05:00
enum LspResponse < V >
where
V : FnOnce ( Value ) ,
{
2020-12-21 08:44:26 -05:00
None ,
2021-03-09 21:41:35 -05:00
Delay ( u64 ) ,
2020-12-21 08:44:26 -05:00
RequestAny ,
Request ( u64 , Value ) ,
2021-01-26 19:32:49 -05:00
RequestAssert ( V ) ,
2021-02-04 13:53:02 -05:00
RequestFixture ( u64 , String ) ,
2020-12-21 08:44:26 -05:00
}
2021-04-19 17:11:23 -04:00
enum LspFixture {
None ,
Path ( & 'static str ) ,
Value ( Value ) ,
}
type LspTestHarnessRequest = ( LspFixture , LspResponse < fn ( Value ) > ) ;
2021-01-26 19:32:49 -05:00
2020-12-21 08:44:26 -05:00
struct LspTestHarness {
2021-01-26 19:32:49 -05:00
requests : Vec < LspTestHarnessRequest > ,
2020-12-21 08:44:26 -05:00
service : Spawn < LspService > ,
}
impl LspTestHarness {
2021-01-26 19:32:49 -05:00
pub fn new ( requests : Vec < LspTestHarnessRequest > ) -> Self {
2020-12-21 08:44:26 -05:00
let ( service , _ ) = LspService ::new ( LanguageServer ::new ) ;
let service = Spawn ::new ( service ) ;
Self { requests , service }
}
async fn run ( & mut self ) {
2021-04-19 17:11:23 -04:00
for ( value_or_str , expected ) in self . requests . iter ( ) {
2020-12-21 08:44:26 -05:00
assert_eq! ( self . service . poll_ready ( ) , Poll ::Ready ( Ok ( ( ) ) ) ) ;
let fixtures_path = test_util ::root_path ( ) . join ( " cli/tests/lsp " ) ;
assert! ( fixtures_path . is_dir ( ) ) ;
let response : Result < Option < jsonrpc ::Outgoing > , ExitedError > =
2021-04-19 17:11:23 -04:00
match value_or_str {
LspFixture ::None = > Ok ( None ) ,
LspFixture ::Path ( req_path_str ) = > {
let req_path = fixtures_path . join ( req_path_str ) ;
let req_str = fs ::read_to_string ( req_path ) . unwrap ( ) ;
let req : jsonrpc ::Incoming =
serde_json ::from_str ( & req_str ) . unwrap ( ) ;
self . service . call ( req ) . await
}
LspFixture ::Value ( value ) = > {
let req : jsonrpc ::Incoming =
serde_json ::from_value ( value . clone ( ) ) . unwrap ( ) ;
self . service . call ( req ) . await
}
2021-03-09 21:41:35 -05:00
} ;
2020-12-21 08:44:26 -05:00
match response {
Ok ( result ) = > match expected {
LspResponse ::None = > assert_eq! ( result , None ) ,
2021-03-09 21:41:35 -05:00
LspResponse ::Delay ( millis ) = > {
tokio ::time ::sleep ( tokio ::time ::Duration ::from_millis ( * millis ) )
. await
}
2020-12-21 08:44:26 -05:00
LspResponse ::RequestAny = > match result {
Some ( jsonrpc ::Outgoing ::Response ( _ ) ) = > ( ) ,
_ = > panic! ( " unexpected result: {:?} " , result ) ,
} ,
LspResponse ::Request ( id , value ) = > match result {
Some ( jsonrpc ::Outgoing ::Response ( resp ) ) = > assert_eq! (
resp ,
jsonrpc ::Response ::ok ( jsonrpc ::Id ::Number ( * id ) , value . clone ( ) )
) ,
_ = > panic! ( " unexpected result: {:?} " , result ) ,
} ,
2021-01-26 19:32:49 -05:00
LspResponse ::RequestAssert ( assert ) = > match result {
Some ( jsonrpc ::Outgoing ::Response ( resp ) ) = > assert ( json! ( resp ) ) ,
_ = > panic! ( " unexpected result: {:?} " , result ) ,
} ,
2021-02-04 13:53:02 -05:00
LspResponse ::RequestFixture ( id , res_path_str ) = > {
let res_path = fixtures_path . join ( res_path_str ) ;
let res_str = fs ::read_to_string ( res_path ) . unwrap ( ) ;
match result {
Some ( jsonrpc ::Outgoing ::Response ( resp ) ) = > assert_eq! (
resp ,
jsonrpc ::Response ::ok (
jsonrpc ::Id ::Number ( * id ) ,
serde_json ::from_str ( & res_str ) . unwrap ( )
)
) ,
_ = > panic! ( " unexpected result: {:?} " , result ) ,
}
}
2020-12-21 08:44:26 -05:00
} ,
Err ( err ) = > panic! ( " Error result: {} " , err ) ,
}
}
}
}
#[ tokio::test ]
async fn test_startup_shutdown ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-21 08:44:26 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-21 08:44:26 -05:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_hover ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request.json " ) ,
2020-12-21 08:44:26 -05:00
LspResponse ::Request (
2 ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const Deno.args: string[] "
} ,
" Returns the script arguments to the program. If for example we run a \n program: \n \n deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd \n \n Then `Deno.args` will contain: \n \n [ \" /etc/passwd \" ] "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
" character " : 21
}
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-21 08:44:26 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-21 08:44:26 -05:00
] ) ;
harness . run ( ) . await ;
}
2020-12-22 00:42:32 -05:00
#[ tokio::test ]
2021-02-06 07:39:01 -05:00
async fn test_hover_asset ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_asset.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " definition_request_asset.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " virtual_text_document_request.json " ) ,
2021-02-06 07:39:01 -05:00
LspResponse ::RequestAny ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " hover_request_asset.json " ) ,
2021-02-06 07:39:01 -05:00
LspResponse ::Request (
2021-02-17 23:37:05 -05:00
5 ,
2021-02-06 07:39:01 -05:00
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " interface Date " ,
} ,
" Enables basic storage and retrieval of dates and times. "
] ,
" range " : {
" start " : {
" line " : 109 ,
" character " : 10 ,
} ,
" end " : {
" line " : 109 ,
" character " : 14 ,
}
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-06 07:39:01 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-06 07:39:01 -05:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
2020-12-22 00:42:32 -05:00
async fn test_hover_disabled ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request_disabled.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request.json " ) ,
LspResponse ::Request ( 2 , json! ( null ) ) ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-22 00:42:32 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-22 00:42:32 -05:00
] ) ;
harness . run ( ) . await ;
}
2020-12-22 05:21:18 -05:00
#[ tokio::test ]
async fn test_hover_unstable_disabled ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_unstable.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request.json " ) ,
2020-12-22 05:21:18 -05:00
LspResponse ::Request (
2 ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " any "
}
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
2021-02-24 22:33:09 -05:00
" character " : 27
2020-12-22 05:21:18 -05:00
}
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-22 05:21:18 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-22 05:21:18 -05:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_hover_unstable_enabled ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request_unstable.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_unstable.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request.json " ) ,
2020-12-22 05:21:18 -05:00
LspResponse ::Request (
2 ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
2021-02-24 22:33:09 -05:00
" value " : " function Deno.openPlugin(filename: string): number "
2020-12-22 05:21:18 -05:00
} ,
2021-02-24 22:33:09 -05:00
" **UNSTABLE**: new API, yet to be vetted. \n \n Open and initialize a plugin. \n \n ```ts \n const rid = Deno.openPlugin( \" ./path/to/some/plugin.so \" ); \n const opId = Deno.core.ops()[ \" some_op \" ]; \n const response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4])); \n console.log(`Response from plugin ${response}`); \n ``` \n \n Requires `allow-plugin` permission. \n \n The plugin system is not stable and will change in the future, hence the \n lack of docs. For now take a look at the example \n https://github.com/denoland/deno/tree/master/test_plugin "
2020-12-22 05:21:18 -05:00
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
2021-02-24 22:33:09 -05:00
" character " : 27
2020-12-22 05:21:18 -05:00
}
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-22 05:21:18 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-22 05:21:18 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-01-22 05:03:16 -05:00
#[ tokio::test ]
async fn test_hover_change_mbc ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_mbc.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_change_notification_mbc.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request_mbc.json " ) ,
2021-01-22 05:03:16 -05:00
LspResponse ::Request (
2 ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
2021-03-09 21:41:35 -05:00
" value " : " const b: \" 🦕😃 \" " ,
2021-01-22 05:03:16 -05:00
} ,
" " ,
] ,
" range " : {
" start " : {
" line " : 2 ,
2021-03-09 21:41:35 -05:00
" character " : 15 ,
2021-01-22 05:03:16 -05:00
} ,
" end " : {
" line " : 2 ,
2021-03-09 21:41:35 -05:00
" character " : 16 ,
2021-01-22 05:03:16 -05:00
} ,
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
] ) ;
harness . run ( ) . await ;
}
#[ derive(Deserialize) ]
struct HoverResponse {
pub result : Option < Hover > ,
}
#[ tokio::test ]
async fn test_hover_closed_document ( ) {
let temp_dir = TempDir ::new ( )
. expect ( " could not create temp dir " )
. into_path ( ) ;
let a_path = temp_dir . join ( " a.ts " ) ;
fs ::write ( a_path , r # "export const a = "a";"# )
. expect ( " could not write file " ) ;
let b_path = temp_dir . join ( " b.ts " ) ;
fs ::write ( & b_path , r # "export * from "./a.ts";"# )
. expect ( " could not write file " ) ;
let b_specifier =
Url ::from_file_path ( b_path ) . expect ( " could not convert path " ) ;
let c_path = temp_dir . join ( " c.ts " ) ;
fs ::write ( & c_path , " import { a } from \" ./b.ts \" ; \n console.log(a); \n " )
. expect ( " could not write file " ) ;
let c_specifier =
Url ::from_file_path ( c_path ) . expect ( " could not convert path " ) ;
let mut harness = LspTestHarness ::new ( vec! [
(
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Value ( json! ( {
" jsonrpc " : " 2.0 " ,
" method " : " textDocument/didOpen " ,
" params " : {
" textDocument " : {
" uri " : b_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export * from " . / a . ts " ; " #
}
}
} ) ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Value ( json! ( {
" jsonrpc " : " 2.0 " ,
" method " : " textDocument/didOpen " ,
" params " : {
" textDocument " : {
" uri " : c_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { a } from \" ./b.ts \" ; \n console.log(a); \n " ,
}
}
} ) ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Value ( json! ( {
" jsonrpc " : " 2.0 " ,
" id " : 2 ,
" method " : " textDocument/hover " ,
" params " : {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : {
" line " : 0 ,
" character " : 10
}
}
} ) ) ,
LspResponse ::RequestAssert ( | value | {
let resp : HoverResponse = serde_json ::from_value ( value ) . unwrap ( ) ;
if let Some ( hover ) = resp . result {
assert_eq! (
hover ,
Hover {
contents : HoverContents ::Array ( vec! [
MarkedString ::LanguageString ( LanguageString {
language : " typescript " . to_string ( ) ,
value : " (alias) const a: \" a \" \n import a " . to_string ( )
} ) ,
MarkedString ::String ( " " . to_string ( ) ) ,
] ) ,
range : Some ( Range {
start : Position {
line : 0 ,
character : 9 ,
} ,
end : Position {
line : 0 ,
character : 10 ,
}
} ) ,
}
) ;
} else {
panic! ( " no response " ) ;
}
} ) ,
) ,
(
LspFixture ::Value ( json! ( {
" jsonrpc " : " 2.0 " ,
" method " : " textDocument/didClose " ,
" params " : {
" textDocument " : {
" uri " : b_specifier ,
}
}
} ) ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Value ( json! ( {
" jsonrpc " : " 2.0 " ,
" id " : 4 ,
" method " : " textDocument/hover " ,
" params " : {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : {
" line " : 0 ,
" character " : 10
}
}
} ) ) ,
LspResponse ::RequestAssert ( | value | {
let resp : HoverResponse = serde_json ::from_value ( value ) . unwrap ( ) ;
if let Some ( hover ) = resp . result {
assert_eq! (
hover ,
Hover {
contents : HoverContents ::Array ( vec! [
MarkedString ::LanguageString ( LanguageString {
language : " typescript " . to_string ( ) ,
value : " (alias) const a: \" a \" \n import a " . to_string ( )
} ) ,
MarkedString ::String ( " " . to_string ( ) ) ,
] ) ,
range : Some ( Range {
start : Position {
line : 0 ,
character : 9 ,
} ,
end : Position {
line : 0 ,
character : 10 ,
}
} ) ,
}
) ;
} else {
panic! ( " no response " ) ;
}
} ) ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-01-22 05:03:16 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-01-22 05:03:16 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-04-19 01:11:26 -04:00
#[ tokio::test ]
async fn test_call_hierarchy ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " prepare_call_hierarchy_did_open_notification.json " ) ,
2021-04-19 01:11:26 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " prepare_call_hierarchy_request.json " ) ,
2021-04-19 01:11:26 -04:00
LspResponse ::Request (
2 ,
json! ( [
{
" name " : " baz " ,
" kind " : 6 ,
" detail " : " Bar " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 5 ,
" character " : 2
} ,
" end " : {
" line " : 7 ,
" character " : 3
}
} ,
" selectionRange " : {
" start " : {
" line " : 5 ,
" character " : 2
} ,
" end " : {
" line " : 5 ,
" character " : 5
}
}
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " incoming_calls_request.json " ) ,
2021-04-19 01:11:26 -04:00
LspResponse ::Request (
4 ,
json! ( [
{
" from " : {
" name " : " main " ,
" kind " : 12 ,
" detail " : " " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 10 ,
" character " : 0
} ,
" end " : {
" line " : 13 ,
" character " : 1
}
} ,
" selectionRange " : {
" start " : {
" line " : 10 ,
" character " : 9
} ,
" end " : {
" line " : 10 ,
" character " : 13
}
}
} ,
" fromRanges " : [
{
" start " : {
" line " : 12 ,
" character " : 6
} ,
" end " : {
" line " : 12 ,
" character " : 9
}
}
]
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " outgoing_calls_request.json " ) ,
2021-04-19 01:11:26 -04:00
LspResponse ::Request (
5 ,
json! ( [
{
" to " : {
" name " : " foo " ,
" kind " : 12 ,
" detail " : " " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 1
}
} ,
" selectionRange " : {
" start " : {
" line " : 0 ,
" character " : 9
} ,
" end " : {
" line " : 0 ,
" character " : 12
}
}
} ,
" fromRanges " : [
{
" start " : {
" line " : 6 ,
" character " : 11
} ,
" end " : {
" line " : 6 ,
" character " : 14
}
}
]
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-04-19 01:11:26 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-04-19 01:11:26 -04:00
] ) ;
harness . run ( ) . await ;
}
2021-01-26 15:50:13 -05:00
#[ tokio::test ]
async fn test_format_mbc ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_mbc_fmt.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " formatting_request_mbc_fmt.json " ) ,
2021-01-26 15:50:13 -05:00
LspResponse ::Request (
2 ,
json! ( [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 12
} ,
" end " : {
" line " : 0 ,
" character " : 13 ,
}
} ,
" newText " : " \" "
} ,
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 21
} ,
" end " : {
" line " : 0 ,
" character " : 22
}
} ,
" newText " : " \" ; "
} ,
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 12 ,
} ,
" end " : {
" line " : 1 ,
" character " : 13 ,
}
} ,
" newText " : " \" "
} ,
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 23 ,
} ,
" end " : {
" line " : 1 ,
" character " : 25 ,
}
} ,
" newText " : " \" ); "
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-01-26 15:50:13 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-01-26 15:50:13 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-01-22 05:03:16 -05:00
#[ tokio::test ]
2021-04-23 12:11:23 -04:00
#[ ignore ] // TODO(ry) Re-enable. Flaky on ubuntu-latest-xl.
2021-01-22 05:03:16 -05:00
async fn test_large_doc_change ( ) {
let mut harness = LspTestHarness ::new ( vec! [
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_large.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_change_notification_large.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_change_notification_large_02.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_change_notification_large_03.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request_large_01.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " hover_request_large_02.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " hover_request_large_03.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-01-22 05:03:16 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-01-22 05:03:16 -05:00
] ) ;
let time = Instant ::now ( ) ;
harness . run ( ) . await ;
assert! (
2021-03-09 21:41:35 -05:00
time . elapsed ( ) . as_millis ( ) < = 10000 ,
2021-01-22 05:03:16 -05:00
" the execution time exceeded 10000ms "
) ;
}
2021-04-19 21:29:27 -04:00
#[ tokio::test ]
async fn test_document_symbol ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " document_symbol_did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " document_symbol_request.json " ) ,
LspResponse ::Request (
2 ,
json! ( [
{
" name " : " bar " ,
" kind " : 13 ,
" range " : {
" start " : {
" line " : 17 ,
" character " : 4
} ,
" end " : {
" line " : 17 ,
" character " : 26
}
} ,
" selectionRange " : {
" start " : {
" line " : 17 ,
" character " : 4
} ,
" end " : {
" line " : 17 ,
" character " : 7
}
}
} ,
{
" name " : " Bar " ,
" kind " : 5 ,
" range " : {
" start " : {
" line " : 4 ,
" character " : 0
} ,
" end " : {
" line " : 13 ,
" character " : 1
}
} ,
" selectionRange " : {
" start " : {
" line " : 4 ,
" character " : 6
} ,
" end " : {
" line " : 4 ,
" character " : 9
}
} ,
" children " : [
{
" name " : " constructor " ,
" kind " : 9 ,
" range " : {
" start " : {
" line " : 5 ,
" character " : 2
} ,
" end " : {
" line " : 5 ,
" character " : 35
}
} ,
" selectionRange " : {
" start " : {
" line " : 5 ,
" character " : 2
} ,
" end " : {
" line " : 5 ,
" character " : 35
}
}
} ,
{
" name " : " baz " ,
" kind " : 6 ,
" tags " : [
1
] ,
" range " : {
" start " : {
" line " : 8 ,
" character " : 2
} ,
" end " : {
" line " : 8 ,
" character " : 25
}
} ,
" selectionRange " : {
" start " : {
" line " : 8 ,
" character " : 2
} ,
" end " : {
" line " : 8 ,
" character " : 5
}
}
} ,
{
" name " : " foo " ,
" kind " : 6 ,
" range " : {
" start " : {
" line " : 6 ,
" character " : 2
} ,
" end " : {
" line " : 6 ,
" character " : 24
}
} ,
" selectionRange " : {
" start " : {
" line " : 6 ,
" character " : 2
} ,
" end " : {
" line " : 6 ,
" character " : 5
}
}
} ,
{
" name " : " getStaticBar " ,
" kind " : 6 ,
" range " : {
" start " : {
" line " : 12 ,
" character " : 2
} ,
" end " : {
" line " : 12 ,
" character " : 57
}
} ,
" selectionRange " : {
" start " : {
" line " : 12 ,
" character " : 17
} ,
" end " : {
" line " : 12 ,
" character " : 29
}
}
} ,
{
" name " : " staticBar " ,
" kind " : 7 ,
" range " : {
" start " : {
" line " : 11 ,
" character " : 2
} ,
" end " : {
" line " : 11 ,
" character " : 32
}
} ,
" selectionRange " : {
" start " : {
" line " : 11 ,
" character " : 9
} ,
" end " : {
" line " : 11 ,
" character " : 18
}
}
} ,
{
" name " : " value " ,
" kind " : 7 ,
" range " : {
" start " : {
" line " : 9 ,
" character " : 2
} ,
" end " : {
" line " : 9 ,
" character " : 35
}
} ,
" selectionRange " : {
" start " : {
" line " : 9 ,
" character " : 6
} ,
" end " : {
" line " : 9 ,
" character " : 11
}
}
} ,
{
" name " : " value " ,
" kind " : 7 ,
" range " : {
" start " : {
" line " : 10 ,
" character " : 2
} ,
" end " : {
" line " : 10 ,
" character " : 42
}
} ,
" selectionRange " : {
" start " : {
" line " : 10 ,
" character " : 6
} ,
" end " : {
" line " : 10 ,
" character " : 11
}
}
} ,
{
" name " : " x " ,
" kind " : 7 ,
" range " : {
" start " : {
" line " : 5 ,
" character " : 14
} ,
" end " : {
" line " : 5 ,
" character " : 30
}
} ,
" selectionRange " : {
" start " : {
" line " : 5 ,
" character " : 21
} ,
" end " : {
" line " : 5 ,
" character " : 22
}
}
}
]
} ,
{
" name " : " IFoo " ,
" kind " : 11 ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 1
}
} ,
" selectionRange " : {
" start " : {
" line " : 0 ,
" character " : 10
} ,
" end " : {
" line " : 0 ,
" character " : 14
}
} ,
" children " : [
{
" name " : " foo " ,
" kind " : 6 ,
" range " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 1 ,
" character " : 17
}
} ,
" selectionRange " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 1 ,
" character " : 5
}
}
}
]
} ,
{
" name " : " Values " ,
" kind " : 10 ,
" range " : {
" start " : {
" line " : 15 ,
" character " : 0
} ,
" end " : {
" line " : 15 ,
" character " : 30
}
} ,
" selectionRange " : {
" start " : {
" line " : 15 ,
" character " : 5
} ,
" end " : {
" line " : 15 ,
" character " : 11
}
} ,
" children " : [
{
" name " : " value1 " ,
" kind " : 13 ,
" range " : {
" start " : {
" line " : 15 ,
" character " : 14
} ,
" end " : {
" line " : 15 ,
" character " : 20
}
} ,
" selectionRange " : {
" start " : {
" line " : 15 ,
" character " : 14
} ,
" end " : {
" line " : 15 ,
" character " : 20
}
}
} ,
{
" name " : " value2 " ,
" kind " : 13 ,
" range " : {
" start " : {
" line " : 15 ,
" character " : 22
} ,
" end " : {
" line " : 15 ,
" character " : 28
}
} ,
" selectionRange " : {
" start " : {
" line " : 15 ,
" character " : 22
} ,
" end " : {
" line " : 15 ,
" character " : 28
}
}
}
]
}
] ) ,
) ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
] ) ;
harness . run ( ) . await ;
}
2021-04-02 02:21:07 -04:00
#[ tokio::test ]
async fn test_folding_range ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " folding_range_did_open_notification.json " ) ,
2021-04-02 02:21:07 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " folding_range_request.json " ) ,
2021-04-02 02:21:07 -04:00
LspResponse ::Request (
2 ,
json! ( [
{
" startLine " : 0 ,
" endLine " : 12 ,
" kind " : " region "
} ,
{
" startLine " : 1 ,
" endLine " : 3 ,
" kind " : " comment "
} ,
{
" startLine " : 4 ,
" endLine " : 10
} ,
{
" startLine " : 5 ,
" endLine " : 9
} ,
{
" startLine " : 6 ,
" endLine " : 7
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-04-02 02:21:07 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-04-02 02:21:07 -04:00
] ) ;
harness . run ( ) . await ;
}
2020-12-29 19:58:20 -05:00
#[ tokio::test ]
async fn test_rename ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " rename_did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " rename_request.json " ) ,
2020-12-29 19:58:20 -05:00
LspResponse ::Request (
2 ,
json! ( {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : {
" line " : 0 ,
" character " : 4
} ,
" end " : {
" line " : 0 ,
" character " : 12
}
} ,
" newText " : " variable_modified "
} , {
" range " : {
" start " : {
" line " : 1 ,
" character " : 12
} ,
" end " : {
" line " : 1 ,
" character " : 20
}
} ,
" newText " : " variable_modified "
} ]
} ]
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-03-23 19:33:25 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-03-23 19:33:25 -04:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_selection_range ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " selection_range_did_open_notification.json " ) ,
2021-03-23 19:33:25 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " selection_range_request.json " ) ,
2021-03-23 19:33:25 -04:00
LspResponse ::Request (
2 ,
json! ( [ {
" range " : {
" start " : {
" line " : 2 ,
" character " : 8
} ,
" end " : {
" line " : 2 ,
" character " : 9
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 2 ,
" character " : 8
} ,
" end " : {
" line " : 2 ,
" character " : 15
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 2 ,
" character " : 4
} ,
" end " : {
" line " : 4 ,
" character " : 5
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 1 ,
" character " : 13
} ,
" end " : {
" line " : 6 ,
" character " : 2
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 6 ,
" character " : 3
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 0 ,
" character " : 11
} ,
" end " : {
" line " : 7 ,
" character " : 0
}
} ,
" parent " : {
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 7 ,
" character " : 1
}
}
}
}
}
}
}
}
} ] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2020-12-29 19:58:20 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2020-12-29 19:58:20 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-01-26 19:32:49 -05:00
2021-04-19 21:26:36 -04:00
#[ tokio::test ]
#[ rustfmt::skip ]
async fn test_semantic_tokens ( ) {
let mut harness = LspTestHarness ::new ( vec! [
( LspFixture ::Path ( " initialize_request.json " ) , LspResponse ::RequestAny ) ,
( LspFixture ::Path ( " initialized_notification.json " ) , LspResponse ::None ) ,
(
LspFixture ::Path ( " semantic_tokens_did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " semantic_tokens_full_request.json " ) ,
LspResponse ::Request (
2 ,
json! ( {
" data " : [ 0 , 5 , 6 , 1 , 1 , 0 , 9 , 6 , 8 , 9 , 0 , 8 , 6 , 8 , 9 , 2 , 15 , 3 , 10 , 5 , 0 , 4 , 1 , 6 , 1 , 0 , 12 , 7 , 2 , 16 , 1 , 8 , 1 , 7 , 41 , 0 , 4 , 1 , 6 , 0 , 0 , 2 , 5 , 11 , 16 , 1 , 9 , 1 , 7 , 40 , 3 , 10 , 4 , 2 , 1 , 1 , 11 , 1 , 9 , 9 , 1 , 2 , 3 , 11 , 1 , 3 , 6 , 3 , 0 , 1 , 0 , 15 , 4 , 2 , 0 , 1 , 30 , 1 , 6 , 9 , 1 , 2 , 3 , 11 , 1 , 1 , 9 , 9 , 9 , 3 , 0 , 16 , 3 , 0 , 0 , 1 , 17 , 12 , 11 , 3 , 0 , 24 , 3 , 0 , 0 , 0 , 4 , 9 , 9 , 2 ]
} ) ,
) ,
) ,
(
LspFixture ::Path ( " semantic_tokens_range_request.json " ) ,
LspResponse ::Request (
4 ,
json! ( {
" data " : [ 0 , 5 , 6 , 1 , 1 , 0 , 9 , 6 , 8 , 9 , 0 , 8 , 6 , 8 , 9 , 2 , 15 , 3 , 10 , 5 , 0 , 4 , 1 , 6 , 1 , 0 , 12 , 7 , 2 , 16 , 1 , 8 , 1 , 7 , 41 , 0 , 4 , 1 , 6 , 0 , 0 , 2 , 5 , 11 , 16 , 1 , 9 , 1 , 7 , 40 ]
} ) ,
) ,
) ,
(
LspFixture ::Path ( " shutdown_request.json " ) ,
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
( LspFixture ::Path ( " exit_notification.json " ) , LspResponse ::None ) ,
] ) ;
harness . run ( ) . await ;
}
2021-01-31 22:30:41 -05:00
#[ tokio::test ]
async fn test_code_lens_request ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
2021-01-31 22:30:41 -05:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " did_open_notification_cl_references.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " code_lens_request.json " ) ,
2021-01-31 22:30:41 -05:00
LspResponse ::Request (
2 ,
json! ( [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 6 ,
} ,
" end " : {
" line " : 0 ,
" character " : 7 ,
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references " ,
} ,
} ,
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 2 ,
} ,
" end " : {
" line " : 1 ,
" character " : 3 ,
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references " ,
}
}
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " code_lens_resolve_request.json " ) ,
2021-01-31 22:30:41 -05:00
LspResponse ::Request (
4 ,
json! ( {
" range " : {
" start " : {
" line " : 0 ,
" character " : 6 ,
} ,
" end " : {
" line " : 0 ,
" character " : 7 ,
}
} ,
" command " : {
" title " : " 1 reference " ,
" command " : " deno.showReferences " ,
" arguments " : [
" file:///a/file.ts " ,
{
" line " : 0 ,
" character " : 6 ,
} ,
[
{
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 12 ,
" character " : 14 ,
} ,
" end " : {
" line " : 12 ,
" character " : 15 ,
}
}
}
] ,
]
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-01-31 22:30:41 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-01-31 22:30:41 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-02-15 21:34:09 -05:00
#[ tokio::test ]
async fn test_signature_help ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
2021-02-15 21:34:09 -05:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " signature_help_did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " signature_help_request_01.json " ) ,
2021-02-15 21:34:09 -05:00
LspResponse ::Request (
1 ,
json! ( {
" signatures " : [
{
" label " : " add(a: number, b: number): number " ,
" documentation " : " Adds two numbers. " ,
" parameters " : [
{
" label " : " a: number " ,
" documentation " : " This is a first number. "
} ,
{
" label " : " b: number " ,
" documentation " : " This is a second number. "
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 0
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " signature_help_did_change_notification.json " ) ,
2021-02-15 21:34:09 -05:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " signature_help_request_02.json " ) ,
2021-02-15 21:34:09 -05:00
LspResponse ::Request (
2 ,
json! ( {
" signatures " : [
{
" label " : " add(a: number, b: number): number " ,
" documentation " : " Adds two numbers. " ,
" parameters " : [
{
" label " : " a: number " ,
" documentation " : " This is a first number. "
} ,
{
" label " : " b: number " ,
" documentation " : " This is a second number. "
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 1
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-15 21:34:09 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-15 21:34:09 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-02-08 05:45:10 -05:00
#[ tokio::test ]
async fn test_code_lens_impl_request ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_cl_impl.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " code_lens_request.json " ) ,
2021-02-08 05:45:10 -05:00
LspResponse ::Request (
2 ,
json! ( [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 10 ,
} ,
" end " : {
" line " : 0 ,
" character " : 11 ,
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations " ,
} ,
} ,
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 10 ,
} ,
" end " : {
" line " : 0 ,
" character " : 11 ,
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references " ,
} ,
} ,
{
" range " : {
" start " : {
" line " : 4 ,
" character " : 6 ,
} ,
" end " : {
" line " : 4 ,
" character " : 7 ,
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references " ,
} ,
} ,
] ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " code_lens_resolve_request_impl.json " ) ,
2021-02-08 05:45:10 -05:00
LspResponse ::Request (
4 ,
json! ( {
" range " : {
" start " : {
" line " : 0 ,
" character " : 10 ,
} ,
" end " : {
" line " : 0 ,
" character " : 11 ,
}
} ,
" command " : {
" title " : " 1 implementation " ,
" command " : " deno.showReferences " ,
" arguments " : [
" file:///a/file.ts " ,
{
" line " : 0 ,
" character " : 10 ,
} ,
[
{
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 4 ,
" character " : 6 ,
} ,
" end " : {
" line " : 4 ,
" character " : 7 ,
}
}
}
] ,
]
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-08 05:45:10 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-08 05:45:10 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-02-09 04:48:53 -05:00
#[ derive(Deserialize) ]
struct CodeLensResponse {
pub result : Option < Vec < CodeLens > > ,
}
#[ derive(Deserialize) ]
struct CodeLensResolveResponse {
pub result : CodeLens ,
}
#[ tokio::test ]
async fn test_code_lens_non_doc_nav_tree ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
2021-02-09 04:48:53 -05:00
LspResponse ::RequestAny ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_asset.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " references_request_asset.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " virtual_text_document_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " code_lens_request_asset.json " ) ,
2021-02-09 04:48:53 -05:00
LspResponse ::RequestAssert ( | value | {
let resp : CodeLensResponse = serde_json ::from_value ( value ) . unwrap ( ) ;
let lenses = resp . result . unwrap ( ) ;
assert! ( lenses . len ( ) > 50 ) ;
} ) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " code_lens_resolve_request_asset.json " ) ,
2021-02-09 04:48:53 -05:00
LspResponse ::RequestAssert ( | value | {
let resp : CodeLensResolveResponse =
serde_json ::from_value ( value ) . unwrap ( ) ;
assert! ( resp . result . command . is_some ( ) ) ;
} ) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-09 04:48:53 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-09 04:48:53 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-02-04 13:53:02 -05:00
#[ tokio::test ]
async fn test_code_actions ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_code_action.json " ) ,
LspResponse ::None ,
) ,
( LspFixture ::None , LspResponse ::Delay ( 500 ) ) ,
(
LspFixture ::Path ( " code_action_request.json " ) ,
2021-02-04 13:53:02 -05:00
LspResponse ::RequestFixture ( 2 , " code_action_response.json " . to_string ( ) ) ,
) ,
2021-02-05 15:10:53 -05:00
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " code_action_resolve_request.json " ) ,
2021-02-05 15:10:53 -05:00
LspResponse ::RequestFixture (
4 ,
" code_action_resolve_request_response.json " . to_string ( ) ,
) ,
) ,
2021-02-04 13:53:02 -05:00
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-04 13:53:02 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-04 13:53:02 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-02-11 23:17:48 -05:00
#[ tokio::test ]
async fn test_code_actions_deno_cache ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_cache.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " code_action_request_cache.json " ) ,
2021-02-11 23:17:48 -05:00
LspResponse ::RequestFixture (
2 ,
" code_action_response_cache.json " . to_string ( ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-03-15 18:01:41 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-03-15 18:01:41 -04:00
] ) ;
harness . run ( ) . await ;
}
#[ derive(Deserialize) ]
struct CompletionResult {
pub result : Option < CompletionResponse > ,
}
#[ tokio::test ]
async fn test_completions ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_completions.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " completion_request.json " ) ,
2021-03-15 18:01:41 -04:00
LspResponse ::RequestAssert ( | value | {
let response : CompletionResult =
serde_json ::from_value ( value ) . unwrap ( ) ;
let result = response . result . unwrap ( ) ;
match result {
CompletionResponse ::List ( list ) = > {
// there should be at least 90 completions for `Deno.`
assert! ( list . items . len ( ) > 90 ) ;
}
_ = > panic! ( " unexpected result " ) ,
}
} ) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " completion_resolve_request.json " ) ,
2021-03-15 18:01:41 -04:00
LspResponse ::Request (
4 ,
json! ( {
" label " : " build " ,
" kind " : 6 ,
" detail " : " const Deno.build: { \n target: string; \n arch: \" x86_64 \" ; \n os: \" darwin \" | \" linux \" | \" windows \" ; \n vendor: string; \n env?: string | undefined; \n } " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " Build related information. "
} ,
" sortText " : " 1 " ,
" insertTextFormat " : 1 ,
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-04-01 05:18:51 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-04-01 05:18:51 -04:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_completions_optional ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification_completion_optional.json " ) ,
2021-04-01 05:18:51 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " completion_request_optional.json " ) ,
2021-04-01 05:18:51 -04:00
LspResponse ::Request (
2 ,
json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " b? " ,
" kind " : 5 ,
" sortText " : " 1 " ,
" filterText " : " b " ,
" insertText " : " b " ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 79 ,
" name " : " b " ,
" useCodeSnippet " : false
}
}
}
]
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " completion_resolve_request_optional.json " ) ,
2021-04-01 05:18:51 -04:00
LspResponse ::Request (
4 ,
json! ( {
" label " : " b? " ,
" kind " : 5 ,
" detail " : " (property) A.b?: string | undefined " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 1 " ,
" filterText " : " b " ,
" insertText " : " b "
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-04-08 21:27:27 -04:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_completions_registry ( ) {
let _g = test_util ::http_server ( ) ;
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request_registry.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " did_open_notification_completion_registry.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " completion_request_registry.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::RequestAssert ( | value | {
let response : CompletionResult =
serde_json ::from_value ( value ) . unwrap ( ) ;
let result = response . result . unwrap ( ) ;
if let CompletionResponse ::List ( list ) = result {
assert_eq! ( list . items . len ( ) , 3 ) ;
} else {
panic! ( " unexpected result " ) ;
}
} ) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " completion_resolve_request_registry.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::Request (
4 ,
json! ( {
" label " : " v2.0.0 " ,
" kind " : 19 ,
" detail " : " (version) " ,
" sortText " : " 0000000003 " ,
" filterText " : " http://localhost:4545/x/a@v2.0.0 " ,
" textEdit " : {
" range " : {
" start " : {
" line " : 0 ,
" character " : 20
} ,
" end " : {
" line " : 0 ,
" character " : 46
}
} ,
" newText " : " http://localhost:4545/x/a@v2.0.0 "
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-04-08 21:27:27 -04:00
] ) ;
harness . run ( ) . await ;
}
#[ tokio::test ]
async fn test_completion_registry_empty_specifier ( ) {
let _g = test_util ::http_server ( ) ;
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request_registry.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::None ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " did_open_notification_completion_registry_02.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " completion_request_registry_02.json " ) ,
2021-04-08 21:27:27 -04:00
LspResponse ::Request (
2 ,
json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " . " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " . "
} ,
{
" label " : " .. " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " .. "
} ,
{
" label " : " http://localhost:4545 " ,
" kind " : 19 ,
" detail " : " (registry) " ,
" sortText " : " 2 " ,
" textEdit " : {
" range " : {
" start " : {
" line " : 0 ,
" character " : 20
} ,
" end " : {
" line " : 0 ,
" character " : 20
}
} ,
" newText " : " http://localhost:4545 "
}
}
]
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-02-11 23:17:48 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-02-11 23:17:48 -05:00
] ) ;
harness . run ( ) . await ;
}
2021-01-26 19:32:49 -05:00
#[ derive(Deserialize) ]
struct PerformanceAverages {
averages : Vec < PerformanceAverage > ,
}
#[ derive(Deserialize) ]
struct PerformanceResponse {
result : PerformanceAverages ,
}
#[ tokio::test ]
async fn test_deno_performance_request ( ) {
let mut harness = LspTestHarness ::new ( vec! [
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " initialize_request.json " ) ,
LspResponse ::RequestAny ,
) ,
(
LspFixture ::Path ( " initialized_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " did_open_notification.json " ) ,
LspResponse ::None ,
) ,
(
LspFixture ::Path ( " hover_request.json " ) ,
2021-01-26 19:32:49 -05:00
LspResponse ::Request (
2 ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const Deno.args: string[] "
} ,
" Returns the script arguments to the program. If for example we run a \n program: \n \n deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd \n \n Then `Deno.args` will contain: \n \n [ \" /etc/passwd \" ] "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
" character " : 21
}
}
} ) ,
) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " performance_request.json " ) ,
2021-01-26 19:32:49 -05:00
LspResponse ::RequestAssert ( | value | {
let resp : PerformanceResponse =
serde_json ::from_value ( value ) . unwrap ( ) ;
2021-03-09 21:41:35 -05:00
// the len can be variable since some of the parts of the language
// server run in separate threads and may not add to performance by
// the time the results are checked.
assert! ( resp . result . averages . len ( ) > = 6 ) ;
2021-01-26 19:32:49 -05:00
} ) ,
) ,
(
2021-04-19 17:11:23 -04:00
LspFixture ::Path ( " shutdown_request.json " ) ,
2021-01-26 19:32:49 -05:00
LspResponse ::Request ( 3 , json! ( null ) ) ,
) ,
2021-04-19 17:11:23 -04:00
(
LspFixture ::Path ( " exit_notification.json " ) ,
LspResponse ::None ,
) ,
2021-01-26 19:32:49 -05:00
] ) ;
harness . run ( ) . await ;
}
2020-12-21 08:44:26 -05:00
}