2022-11-25 20:29:56 -05:00
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
mod lsp {
use deno_ast ::ModuleSpecifier ;
use deno_core ::serde ::de ::DeserializeOwned ;
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 ::url ::Url ;
use pretty_assertions ::assert_eq ;
use std ::collections ::HashSet ;
use std ::fs ;
2022-11-29 19:32:18 -05:00
use std ::process ::Stdio ;
use test_util ::deno_cmd_with_deno_dir ;
2022-11-25 20:29:56 -05:00
use test_util ::deno_exe_path ;
2022-11-29 19:32:18 -05:00
use test_util ::env_vars_for_npm_tests ;
2022-11-25 20:29:56 -05:00
use test_util ::http_server ;
use test_util ::lsp ::LspClient ;
use test_util ::testdata_path ;
use test_util ::TempDir ;
use tower_lsp ::lsp_types as lsp ;
fn load_fixture ( path : & str ) -> Value {
load_fixture_as ( path )
}
fn load_fixture_as < T > ( path : & str ) -> T
where
T : DeserializeOwned ,
{
let fixture_str = load_fixture_str ( path ) ;
serde_json ::from_str ::< T > ( & fixture_str ) . unwrap ( )
}
fn load_fixture_str ( path : & str ) -> String {
let fixtures_path = testdata_path ( ) . join ( " lsp " ) ;
let path = fixtures_path . join ( path ) ;
fs ::read_to_string ( path ) . unwrap ( )
}
fn init ( init_path : & str ) -> LspClient {
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , load_fixture ( init_path ) )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
client
}
fn did_open < V > (
client : & mut LspClient ,
params : V ,
) -> Vec < lsp ::PublishDiagnosticsParams >
where
V : Serialize ,
{
client
. write_notification ( " textDocument/didOpen " , params )
. unwrap ( ) ;
handle_configuration_request (
client ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" test " : true
}
} ] ) ,
) ;
read_diagnostics ( client ) . 0
}
fn handle_configuration_request ( client : & mut LspClient , result : Value ) {
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client . write_response ( id , result ) . unwrap ( ) ;
}
fn read_diagnostics ( client : & mut LspClient ) -> CollectedDiagnostics {
// diagnostics come in batches of three unless they're cancelled
let mut diagnostics = vec! [ ] ;
for _ in 0 .. 3 {
let ( method , response ) = client
. read_notification ::< lsp ::PublishDiagnosticsParams > ( )
. unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
diagnostics . push ( response . unwrap ( ) ) ;
}
CollectedDiagnostics ( diagnostics )
}
fn shutdown ( client : & mut LspClient ) {
client
. write_request ::< _ , _ , Value > ( " shutdown " , json! ( null ) )
. unwrap ( ) ;
client . write_notification ( " exit " , json! ( null ) ) . unwrap ( ) ;
}
pub fn ensure_directory_specifier (
mut specifier : ModuleSpecifier ,
) -> ModuleSpecifier {
let path = specifier . path ( ) ;
if ! path . ends_with ( '/' ) {
let new_path = format! ( " {} / " , path ) ;
specifier . set_path ( & new_path ) ;
}
specifier
}
struct TestSession {
client : LspClient ,
open_file_count : usize ,
}
impl TestSession {
pub fn from_file ( init_path : & str ) -> Self {
Self ::from_client ( init ( init_path ) )
}
pub fn from_client ( client : LspClient ) -> Self {
Self {
client ,
open_file_count : 0 ,
}
}
pub fn did_open < V > ( & mut self , params : V ) -> CollectedDiagnostics
where
V : Serialize ,
{
self
. client
. write_notification ( " textDocument/didOpen " , params )
. unwrap ( ) ;
let ( id , method , _ ) = self . client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
self
. client
. write_response (
id ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" test " : true
}
} ] ) ,
)
. unwrap ( ) ;
self . open_file_count + = 1 ;
self . read_diagnostics ( )
}
pub fn read_diagnostics ( & mut self ) -> CollectedDiagnostics {
let mut all_diagnostics = Vec ::new ( ) ;
for _ in 0 .. self . open_file_count {
all_diagnostics . extend ( read_diagnostics ( & mut self . client ) . 0 ) ;
}
CollectedDiagnostics ( all_diagnostics )
}
pub fn shutdown_and_exit ( & mut self ) {
shutdown ( & mut self . client ) ;
}
}
#[ derive(Debug, Clone) ]
struct CollectedDiagnostics ( Vec < lsp ::PublishDiagnosticsParams > ) ;
impl CollectedDiagnostics {
/// Gets the diagnostics that the editor will see after all the publishes.
pub fn viewed ( & self ) -> Vec < lsp ::Diagnostic > {
self
. viewed_messages ( )
. into_iter ( )
. flat_map ( | m | m . diagnostics )
. collect ( )
}
/// Gets the messages that the editor will see after all the publishes.
pub fn viewed_messages ( & self ) -> Vec < lsp ::PublishDiagnosticsParams > {
// go over the publishes in reverse order in order to get
// the final messages that will be shown in the editor
let mut messages = Vec ::new ( ) ;
let mut had_specifier = HashSet ::new ( ) ;
for message in self . 0. iter ( ) . rev ( ) {
if had_specifier . insert ( message . uri . clone ( ) ) {
messages . insert ( 0 , message . clone ( ) ) ;
}
}
messages
}
pub fn with_source ( & self , source : & str ) -> lsp ::PublishDiagnosticsParams {
self
. viewed_messages ( )
. iter ( )
. find ( | p | {
p . diagnostics
. iter ( )
. any ( | d | d . source = = Some ( source . to_string ( ) ) )
} )
. map ( ToOwned ::to_owned )
. unwrap ( )
}
pub fn with_file_and_source (
& self ,
specifier : & str ,
source : & str ,
) -> lsp ::PublishDiagnosticsParams {
let specifier = ModuleSpecifier ::parse ( specifier ) . unwrap ( ) ;
self
. viewed_messages ( )
. iter ( )
. find ( | p | {
p . uri = = specifier
& & p
. diagnostics
. iter ( )
. any ( | d | d . source = = Some ( source . to_string ( ) ) )
} )
. map ( ToOwned ::to_owned )
. unwrap ( )
}
}
#[ test ]
fn lsp_startup_shutdown ( ) {
let mut client = init ( " initialize_params.json " ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_init_tsconfig ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let tsconfig =
serde_json ::to_vec_pretty ( & load_fixture ( " lib.tsconfig.json " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " lib.tsconfig.json " ) , tsconfig ) . unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./lib.tsconfig.json " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " location.pathname; \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_tsconfig_types ( ) {
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let temp_dir = TempDir ::new ( ) ;
let tsconfig =
serde_json ::to_vec_pretty ( & load_fixture ( " types.tsconfig.json " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " types.tsconfig.json " ) , tsconfig ) . unwrap ( ) ;
let a_dts = load_fixture_str ( " a.d.ts " ) ;
fs ::write ( temp_dir . path ( ) . join ( " a.d.ts " ) , a_dts ) . unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./types.tsconfig.json " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : Url ::from_file_path ( temp_dir . path ( ) . join ( " test.ts " ) ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(a); \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_tsconfig_bad_config_path ( ) {
let mut client = init ( " initialize_params_bad_config_option.json " ) ;
let ( method , maybe_params ) = client . read_notification ( ) . unwrap ( ) ;
assert_eq! ( method , " window/showMessage " ) ;
assert_eq! ( maybe_params , Some ( lsp ::ShowMessageParams {
typ : lsp ::MessageType ::WARNING ,
message : " The path to the configuration file ( \" bad_tsconfig.json \" ) is not resolvable. " . to_string ( )
} ) ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
}
#[ test ]
fn lsp_triple_slash_types ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let a_dts = load_fixture_str ( " a.d.ts " ) ;
fs ::write ( temp_dir . path ( ) . join ( " a.d.ts " ) , a_dts ) . unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : Url ::from_file_path ( temp_dir . path ( ) . join ( " test.ts " ) ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /// <reference types= \" ./a.d.ts \" /> \n \n console.log(a); \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_import_map ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let import_map =
serde_json ::to_vec_pretty ( & load_fixture ( " import-map.json " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " import-map.json " ) , import_map ) . unwrap ( ) ;
fs ::create_dir ( temp_dir . path ( ) . join ( " lib " ) ) . unwrap ( ) ;
fs ::write (
temp_dir . path ( ) . join ( " lib " ) . join ( " b.ts " ) ,
r # "export const b = "b";"# ,
)
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " importMap " . to_string ( ) , json! ( " import-map.json " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let uri = Url ::from_file_path ( temp_dir . path ( ) . join ( " a.ts " ) ) . unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : {
" line " : 2 ,
" character " : 12
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
" start " : {
" line " : 2 ,
" character " : 12
} ,
" end " : {
" line " : 2 ,
" character " : 13
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_import_map_data_url ( ) {
let mut client = init ( " initialize_params_import_map.json " ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import example from \" example \" ; \n "
}
} ) ,
) ;
let mut diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
// This indicates that the import map from initialize_params_import_map.json
// is applied correctly.
assert! ( diagnostics . any ( | diagnostic | diagnostic . code
= = Some ( lsp ::NumberOrString ::String ( " no-cache " . to_string ( ) ) )
& & diagnostic
. message
. contains ( " https://deno.land/x/example/mod.ts " ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_import_map_config_file ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_import_map_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.import_map.jsonc " ) )
. unwrap ( ) ;
fs ::write (
temp_dir . path ( ) . join ( " deno.import_map.jsonc " ) ,
deno_import_map_jsonc ,
)
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./deno.import_map.jsonc " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let import_map =
serde_json ::to_vec_pretty ( & load_fixture ( " import-map.json " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " import-map.json " ) , import_map ) . unwrap ( ) ;
fs ::create_dir ( temp_dir . path ( ) . join ( " lib " ) ) . unwrap ( ) ;
fs ::write (
temp_dir . path ( ) . join ( " lib " ) . join ( " b.ts " ) ,
r # "export const b = "b";"# ,
)
. unwrap ( ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let uri = Url ::from_file_path ( temp_dir . path ( ) . join ( " a.ts " ) ) . unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ,
) ;
let diagnostics = diagnostics . into_iter ( ) . flat_map ( | x | x . diagnostics ) ;
assert_eq! ( diagnostics . count ( ) , 0 ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : {
" line " : 2 ,
" character " : 12
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
" start " : {
" line " : 2 ,
" character " : 12
} ,
" end " : {
" line " : 2 ,
" character " : 13
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_deno_task ( ) {
let temp_dir = TempDir ::new ( ) ;
let workspace_root = temp_dir . path ( ) . canonicalize ( ) . unwrap ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
fs ::write (
workspace_root . join ( " deno.jsonc " ) ,
r #" {
" tasks " : {
" build " : " deno test " ,
" some:test " : " deno bundle mod.ts "
}
} " #,
)
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( workspace_root ) . unwrap ( ) ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > ( " deno/task " , json! ( null ) )
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [
{
" name " : " build " ,
" detail " : " deno test "
} ,
{
" name " : " some:test " ,
" detail " : " deno bundle mod.ts "
}
] ) )
) ;
}
#[ test ]
fn lsp_import_assertions ( ) {
let mut client = init ( " initialize_params_import_map.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/test.json " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" a \" :1} "
}
} ) ,
)
. unwrap ( ) ;
handle_configuration_request (
& mut client ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" test " : true
}
} ] ) ,
) ;
let diagnostics = CollectedDiagnostics ( did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/a.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import a from \" ./test.json \" ; \n \n console.log(a); \n "
}
} ) ,
) ) ;
assert_eq! (
json! (
diagnostics
. with_file_and_source ( " file:///a/a.ts " , " deno " )
. diagnostics
) ,
json! ( [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 14
} ,
" end " : {
" line " : 0 ,
" character " : 27
}
} ,
" severity " : 1 ,
" code " : " no-assert-type " ,
" source " : " deno " ,
" message " : " The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \" json \" }` to the import statement. "
}
] )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_params_import_assertion.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_response_import_assertion.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_import_map_import_completions ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let import_map =
serde_json ::to_vec_pretty ( & load_fixture ( " import-map-completions.json " ) )
. unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " import-map.json " ) , import_map ) . unwrap ( ) ;
fs ::create_dir ( temp_dir . path ( ) . join ( " lib " ) ) . unwrap ( ) ;
fs ::write (
temp_dir . path ( ) . join ( " lib " ) . join ( " b.ts " ) ,
r # "export const b = "b";"# ,
)
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " importMap " . to_string ( ) , json! ( " import-map.json " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let uri = Url ::from_file_path ( temp_dir . path ( ) . join ( " a.ts " ) ) . unwrap ( ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" /~/b.ts \" ; \n import * as b from \" \" "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : {
" line " : 1 ,
" character " : 20
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " \" "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " . " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " . " ,
" commitCharacters " : [ " \" " , " ' " ] ,
} ,
{
" label " : " .. " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " .. " ,
" commitCharacters " : [ " \" " , " ' " ] ,
} ,
{
" label " : " std " ,
" kind " : 19 ,
" detail " : " (import map) " ,
" sortText " : " std " ,
" insertText " : " std " ,
" commitCharacters " : [ " \" " , " ' " ] ,
} ,
{
" label " : " fs " ,
" kind " : 17 ,
" detail " : " (import map) " ,
" sortText " : " fs " ,
" insertText " : " fs " ,
" commitCharacters " : [ " \" " , " ' " ] ,
} ,
{
" label " : " /~ " ,
" kind " : 19 ,
" detail " : " (import map) " ,
" sortText " : " /~ " ,
" insertText " : " /~ " ,
" commitCharacters " : [ " \" " , " ' " ] ,
}
]
} ) )
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : uri ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 20
} ,
" end " : {
" line " : 1 ,
" character " : 20
}
} ,
" text " : " /~/ "
}
]
} ) ,
)
. unwrap ( ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : {
" line " : 1 ,
" character " : 23
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " / "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " b.ts " ,
" kind " : 9 ,
" detail " : " (import map) " ,
" sortText " : " 1 " ,
" filterText " : " /~/b.ts " ,
" textEdit " : {
" range " : {
" start " : {
" line " : 1 ,
" character " : 20
} ,
" end " : {
" line " : 1 ,
" character " : 23
}
} ,
" newText " : " /~/b.ts "
} ,
" commitCharacters " : [ " \" " , " ' " ] ,
}
]
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const Deno.args: string[] "
} ,
" Returns the script arguments to the program. \n \n Give the following command line invocation of Deno: \n \n ```sh \n deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd \n ``` \n \n Then `Deno.args` will contain: \n \n ``` \n [ \" /etc/passwd \" ] \n ``` \n \n If you are looking for a structured way to parse arguments, there is the \n [`std/flags`](https://deno.land/std/flags) module as part of the Deno \n standard library. " ,
" \n \n *@category* - Runtime Environment " ,
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
" character " : 21
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_asset ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
let ( _ , maybe_error ) = client
. write_request ::< _ , _ , Value > (
" textDocument/definition " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 14
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
let ( _ , maybe_error ) = client
. write_request ::< _ , _ , Value > (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.es2015.symbol.wellknown.d.ts "
} ,
" position " : {
" line " : 109 ,
" character " : 13
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( 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 ,
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_disabled ( ) {
let mut client = init ( " initialize_params_disabled.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
)
. unwrap ( ) ;
handle_configuration_request ( & mut client , json! ( [ { " enable " : false } ] ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_inlay_hints ( ) {
let mut client = init ( " initialize_params_hints.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" function a(b: string) {
return b ;
}
a ( " foo " ) ;
enum C {
A ,
}
parseInt ( " 123 " , 8 ) ;
const d = Date . now ( ) ;
class E {
f = Date . now ( ) ;
}
[ " a " ] . map ( ( v ) = > v + v ) ;
" #
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/inlayHint " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 19 ,
" character " : 0 ,
}
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
json! ( maybe_res ) ,
json! ( [
{
" position " : {
" line " : 0 ,
" character " : 21
} ,
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
} ,
{
" position " : {
" line " : 4 ,
" character " : 10
} ,
" label " : " b: " ,
" kind " : 2 ,
" paddingRight " : true
} ,
{
" position " : {
" line " : 7 ,
" character " : 11
} ,
" label " : " = 0 " ,
" paddingLeft " : true
} ,
{
" position " : {
" line " : 10 ,
" character " : 17
} ,
" label " : " string: " ,
" kind " : 2 ,
" paddingRight " : true
} ,
{
" position " : {
" line " : 10 ,
" character " : 24
} ,
" label " : " radix: " ,
" kind " : 2 ,
" paddingRight " : true
} ,
{
" position " : {
" line " : 12 ,
" character " : 15
} ,
" label " : " : number " ,
" kind " : 1 ,
" paddingLeft " : true
} ,
{
" position " : {
" line " : 15 ,
" character " : 11
} ,
" label " : " : number " ,
" kind " : 1 ,
" paddingLeft " : true
} ,
{
" position " : {
" line " : 18 ,
" character " : 18
} ,
" label " : " callbackfn: " ,
" kind " : 2 ,
" paddingRight " : true
} ,
{
" position " : {
" line " : 18 ,
" character " : 20
} ,
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
} ,
{
" position " : {
" line " : 18 ,
" character " : 21
} ,
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
}
] )
) ;
}
#[ test ]
fn lsp_inlay_hints_not_enabled ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" function a(b: string) {
return b ;
}
a ( " foo " ) ;
enum C {
A ,
}
parseInt ( " 123 " , 8 ) ;
const d = Date . now ( ) ;
class E {
f = Date . now ( ) ;
}
[ " a " ] . map ( ( v ) = > v + v ) ;
" #
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/inlayHint " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 19 ,
" character " : 0 ,
}
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( json! ( maybe_res ) , json! ( null ) ) ;
}
#[ test ]
fn lsp_workspace_enable_paths ( ) {
let mut params : lsp ::InitializeParams = serde_json ::from_value (
load_fixture ( " initialize_params_workspace_enable_paths.json " ) ,
)
. unwrap ( ) ;
// we aren't actually writing anything to the tempdir in this test, but we
// just need a legitimate file path on the host system so that logic that
// tries to convert to and from the fs paths works on all env
let temp_dir = TempDir ::new ( ) ;
let root_specifier =
ensure_directory_specifier ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
params . root_uri = Some ( root_specifier . clone ( ) ) ;
params . workspace_folders = Some ( vec! [ lsp ::WorkspaceFolder {
uri : root_specifier . clone ( ) ,
name : " project " . to_string ( ) ,
} ] ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
handle_configuration_request (
& mut client ,
json! ( [ {
" enable " : false ,
" enablePaths " : [
" ./worker "
] ,
} ] ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/subdir/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./file.ts " ) . unwrap ( ) ,
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
2022-12-07 12:59:59 -05:00
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
2022-11-25 20:29:56 -05:00
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17 ,
} ,
" end " : {
" line " : 0 ,
" character " : 20 ,
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/subdir/file.ts " ) . unwrap ( ) ,
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
2022-12-07 12:59:59 -05:00
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
2022-11-25 20:29:56 -05:00
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17 ,
} ,
" end " : {
" line " : 0 ,
" character " : 20 ,
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_unstable_disabled ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.dlopen); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " any "
}
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 17
} ,
" end " : {
" line " : 0 ,
" character " : 23
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_unstable_enabled ( ) {
let mut client = init ( " initialize_params_unstable.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.ppid); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " :[
{
" language " :" typescript " ,
" value " :" const Deno.ppid: number "
} ,
" The process ID of parent process of this instance of the Deno CLI. \n \n ```ts \n console.log(Deno.ppid); \n ``` " ,
" \n \n *@category* - Runtime Environment " ,
] ,
" range " :{
" start " :{
" line " :0 ,
" character " :17
} ,
" end " :{
" line " :0 ,
" character " :21
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_change_mbc ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const a = `编写软件很难`; \n const b = `👍🦕😃`; \n console.log(a, b); \n "
}
} ) ,
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 11
} ,
" end " : {
" line " : 1 ,
// the LSP uses utf16 encoded characters indexes, so
// after the deno emoiji is character index 15
" character " : 15
}
} ,
" text " : " "
}
]
} ) ,
)
. unwrap ( ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 2 ,
" character " : 15
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const b: \" 😃 \" " ,
} ,
" " ,
] ,
" range " : {
" start " : {
" line " : 2 ,
" character " : 15 ,
} ,
" end " : {
" line " : 2 ,
" character " : 16 ,
} ,
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_closed_document ( ) {
let temp_dir_guard = TempDir ::new ( ) ;
let temp_dir = temp_dir_guard . path ( ) ;
let a_path = temp_dir . join ( " a.ts " ) ;
fs ::write ( a_path , r # "export const a = "a";"# ) . unwrap ( ) ;
let b_path = temp_dir . join ( " b.ts " ) ;
fs ::write ( & b_path , r # "export * from "./a.ts";"# ) . unwrap ( ) ;
let b_specifier = Url ::from_file_path ( b_path ) . unwrap ( ) ;
let c_path = temp_dir . join ( " c.ts " ) ;
fs ::write ( & c_path , " import { a } from \" ./b.ts \" ; \n console.log(a); \n " )
. unwrap ( ) ;
let c_specifier = Url ::from_file_path ( c_path ) . unwrap ( ) ;
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : b_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export * from " . / a . ts " ; " #
}
} ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response ( id , json! ( [ { " enable " : true } ] ) )
. unwrap ( ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : c_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { a } from \" ./b.ts \" ; \n console.log(a); \n " ,
}
} ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response ( id , json! ( [ { " enable " : true } ] ) )
. unwrap ( ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : {
" line " : 0 ,
" character " : 10
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (alias) const a: \" a \" \n import a "
} ,
" "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 9
} ,
" end " : {
" line " : 0 ,
" character " : 10
}
}
} ) )
) ;
client
. write_notification (
" textDocument/didClose " ,
json! ( {
" textDocument " : {
" uri " : b_specifier ,
}
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : {
" line " : 0 ,
" character " : 10
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (alias) const a: \" a \" \n import a "
} ,
" "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 9
} ,
" end " : {
" line " : 0 ,
" character " : 10
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_dependency ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ,
) ;
did_open (
& mut client ,
load_fixture ( " did_open_params_import_hover.json " ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [ ] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 0 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js \n \n **Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts \n "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 19
} ,
" end " :{
" line " : 0 ,
" character " : 62
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 3 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/subdir/type_reference.js \n \n **Types**: http​://127.0.0.1:4545/subdir/type_reference.d.ts \n "
} ,
" range " : {
" start " : {
" line " : 3 ,
" character " : 19
} ,
" end " :{
" line " : 3 ,
" character " : 67
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 4 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/subdir/mod1.ts \n "
} ,
" range " : {
" start " : {
" line " : 4 ,
" character " : 19
} ,
" end " :{
" line " : 4 ,
" character " : 57
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 5 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: _(a data url)_ \n "
} ,
" range " : {
" start " : {
" line " : 5 ,
" character " : 19
} ,
" end " :{
" line " : 5 ,
" character " : 132
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 6 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: file​:///a/file_01.ts \n "
} ,
" range " : {
" start " : {
" line " : 6 ,
" character " : 19
} ,
" end " :{
" line " : 6 ,
" character " : 33
}
}
} ) )
) ;
}
// This tests for a regression covered by denoland/deno#12753 where the lsp was
// unable to resolve dependencies when there was an invalid syntax in the module
#[ test ]
fn lsp_hover_deps_preserved_when_invalid_parse ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file1.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export type Foo = { bar(): string }; \n "
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { Foo } from './file1.ts'; declare const f: Foo; f \n "
}
} ) ,
) ;
let ( maybe_res , maybe_error ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 56
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const f: Foo " ,
} ,
" "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 56 ,
} ,
" end " : {
" line " : 0 ,
" character " : 57 ,
}
}
} ) )
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 57
} ,
" end " : {
" line " : 0 ,
" character " : 58
}
} ,
" text " : " . "
}
]
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_error ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 56
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " const f: Foo " ,
} ,
" "
] ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 56 ,
} ,
" end " : {
" line " : 0 ,
" character " : 57 ,
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_typescript_types ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://127.0.0.1:4545/xTypeScriptTypes.js \" ; \n \n console.log(a.foo); \n " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [
{
" uri " : " http://127.0.0.1:4545/xTypeScriptTypes.js " ,
}
] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 24
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_res . is_some ( ) ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
json! ( maybe_res . unwrap ( ) ) ,
json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js \n \n **Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts \n "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 19
} ,
" end " : {
" line " : 0 ,
" character " : 62
}
}
} )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_hover_jsdoc_symbol_link ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export function hello() {} \n "
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { hello } from \" ./b.ts \" ; \n \n hello(); \n \n const b = \" b \" ; \n \n /** JSDoc {@link hello} and {@linkcode b} */ \n function a() {} \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 7 ,
" character " : 10
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " function a(): void "
} ,
" JSDoc [hello](file:///a/file.ts#L1,10) and [`b`](file:///a/file.ts#L5,7) "
] ,
" range " : {
" start " : {
" line " : 7 ,
" character " : 9
} ,
" end " : {
" line " : 7 ,
" character " : 10
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_goto_type_definition ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " interface A { \n a: string; \n } \n \n export class B implements A { \n a = \" a \" ; \n log() { \n console.log(this.a); \n } \n } \n \n const b = new B(); \n b; \n " ,
}
} ) ,
) ;
let ( maybe_res , maybe_error ) = client
. write_request ::< _ , _ , Value > (
" textDocument/typeDefinition " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 12 ,
" character " : 1
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [
{
" targetUri " : " file:///a/file.ts " ,
" targetRange " : {
" start " : {
" line " : 4 ,
" character " : 0
} ,
" end " : {
" line " : 9 ,
" character " : 1
}
} ,
" targetSelectionRange " : {
" start " : {
" line " : 4 ,
" character " : 13
} ,
" end " : {
" line " : 4 ,
" character " : 14
}
}
}
] ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_call_hierarchy ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " function foo() { \n return false; \n } \n \n class Bar { \n baz() { \n return foo(); \n } \n } \n \n function main() { \n const bar = new Bar(); \n bar.baz(); \n } \n \n main(); "
}
} ) ,
) ;
let ( maybe_res , maybe_error ) = client
. write_request (
" textDocument/prepareCallHierarchy " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 5 ,
" character " : 3
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " prepare_call_hierarchy_response.json " ) )
) ;
let ( maybe_res , maybe_error ) = client
. write_request (
" callHierarchy/incomingCalls " ,
load_fixture ( " incoming_calls_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " incoming_calls_response.json " ) )
) ;
let ( maybe_res , maybe_error ) = client
. write_request (
" callHierarchy/outgoingCalls " ,
load_fixture ( " outgoing_calls_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_error . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " outgoing_calls_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_large_doc_changes ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open ( & mut client , load_fixture ( " did_open_params_large.json " ) ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 444 ,
" character " : 11
} ,
" end " : {
" line " : 444 ,
" character " : 14
}
} ,
" text " : " +++ "
}
]
} ) ,
)
. unwrap ( ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 445 ,
" character " : 4
} ,
" end " : {
" line " : 445 ,
" character " : 4
}
} ,
" text " : " // "
}
]
} ) ,
)
. unwrap ( ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 477 ,
" character " : 4
} ,
" end " : {
" line " : 477 ,
" character " : 9
}
} ,
" text " : " error "
}
]
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 421 ,
" character " : 30
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_res . is_some ( ) ) ;
assert! ( maybe_err . is_none ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 444 ,
" character " : 6
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_res . is_some ( ) ) ;
assert! ( maybe_err . is_none ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 461 ,
" character " : 34
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_res . is_some ( ) ) ;
assert! ( maybe_err . is_none ( ) ) ;
shutdown ( & mut client ) ;
assert! ( client . duration ( ) . as_millis ( ) < = 15000 ) ;
}
#[ test ]
fn lsp_document_symbol ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open ( & mut client , load_fixture ( " did_open_params_doc_symbol.json " ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/documentSymbol " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " document_symbol_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_folding_range ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " // #region 1 \n /* \n * Some comment \n */ \n class Foo { \n bar(a, b) { \n if (a === b) { \n return true; \n } \n return false; \n } \n } \n // #endregion "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/foldingRange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( 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
}
] ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_rename ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
// this should not rename in comments and strings
" text " : " let variable = 'a'; // variable \n console.log(variable); \n \" variable \" ; \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/rename " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 4
} ,
" newName " : " variable_modified "
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( load_fixture ( " rename_response.json " ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_selection_range ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " class Foo { \n bar(a, b) { \n if (a === b) { \n return true; \n } \n return false; \n } \n } "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/selectionRange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" positions " : [
{
" line " : 2 ,
" character " : 8
}
]
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " selection_range_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_semantic_tokens ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
load_fixture ( " did_open_params_semantic_tokens.json " ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( 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
]
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/semanticTokens/range " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 6 ,
" character " : 0
}
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( 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
]
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_lens ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " class A { \n a = \" a \" ; \n \n b() { \n console.log(this.a); \n } \n \n c() { \n this.a = \" c \" ; \n } \n } \n \n const a = new A(); \n a.b(); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( load_fixture ( " code_lens_response.json " ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : {
" line " : 0 ,
" character " : 6
} ,
" end " : {
" line " : 0 ,
" character " : 7
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_resolve_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_lens_impl ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " interface A { \n b(): void; \n } \n \n class B implements A { \n b() { \n console.log( \" b \" ); \n } \n } \n \n interface C { \n c: string; \n } \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_response_impl.json " ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : {
" line " : 0 ,
" character " : 10
} ,
" end " : {
" line " : 0 ,
" character " : 11
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_resolve_response_impl.json " ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : {
" line " : 10 ,
" character " : 10
} ,
" end " : {
" line " : 10 ,
" character " : 11
}
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" range " : {
" start " : {
" line " : 10 ,
" character " : 10
} ,
" end " : {
" line " : 10 ,
" character " : 11
}
} ,
" command " : {
" title " : " 0 implementations " ,
" command " : " "
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_lens_test ( ) {
let mut client = init ( " initialize_params_code_lens_test.json " ) ;
did_open (
& mut client ,
load_fixture ( " did_open_params_test_code_lens.json " ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_response_test.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_lens_test_disabled ( ) {
let mut client = init ( " initialize_params_code_lens_test_disabled.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
load_fixture ( " did_open_params_test_code_lens.json " ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response (
id ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" test " : false
}
} ] ) ,
)
. unwrap ( ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( method , _ ) = client . read_notification ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " textDocument/publishDiagnostics " ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( [ ] ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_lens_non_doc_nav_tree ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 3
} ,
" context " : {
" includeDeclaration " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Vec < lsp ::CodeLens > > (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let res = maybe_res . unwrap ( ) ;
assert! ( res . len ( ) > 50 ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , lsp ::CodeLens > (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : {
" line " : 416 ,
" character " : 12
} ,
" end " : {
" line " : 416 ,
" character " : 19
}
} ,
" data " : {
" specifier " : " asset:///lib.deno.shared_globals.d.ts " ,
" source " : " references "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_nav_tree_updates ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " interface A { \n b(): void; \n } \n \n class B implements A { \n b() { \n console.log( \" b \" ); \n } \n } \n \n interface C { \n c: string; \n } \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_response_impl.json " ) )
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 10 ,
" character " : 0
} ,
" end " : {
" line " : 13 ,
" character " : 0
}
} ,
" text " : " "
}
]
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_lens_response_changed.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_signature_help ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /** \n * Adds two numbers. \n * @param a This is a first number. \n * @param b This is a second number. \n */ \n function add(a: number, b: number) { \n return a + b; \n } \n \n add( "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/signatureHelp " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" character " : 4 ,
" line " : 9
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " ( " ,
" isRetrigger " : false
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" signatures " : [
{
" label " : " add(a: number, b: number): number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " Adds two numbers. "
} ,
" parameters " : [
{
" label " : " a: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a first number. "
}
} ,
{
" label " : " b: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a second number. "
}
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 0
} ) )
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 9 ,
" character " : 4
} ,
" end " : {
" line " : 9 ,
" character " : 4
}
} ,
" text " : " 123, "
}
]
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/signatureHelp " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" character " : 8 ,
" line " : 9
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" signatures " : [
{
" label " : " add(a: number, b: number): number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " Adds two numbers. "
} ,
" parameters " : [
{
" label " : " a: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a first number. "
}
} ,
{
" label " : " b: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a second number. "
}
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 1
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_actions ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export function a(): void { \n await Promise.resolve( \" a \" ); \n } \n \n export function b(): void { \n await Promise.resolve( \" b \" ); \n } \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( load_fixture ( " code_action_response.json " ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" codeAction/resolve " ,
load_fixture ( " code_action_resolve_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_resolve_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_actions_deno_cache ( ) {
let mut session = TestSession ::from_file ( " initialize_params.json " ) ;
let diagnostics = session . did_open ( json! ( {
2022-11-29 19:32:18 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" https://deno.land/x/a/mod.ts \" ; \n \n console.log(a); \n "
}
} ) ) ;
2022-11-25 20:29:56 -05:00
assert_eq! (
diagnostics . with_source ( " deno " ) ,
load_fixture_as ( " diagnostics_deno_deps.json " )
) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_params_cache.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_response_cache.json " ) )
) ;
session . shutdown_and_exit ( ) ;
}
#[ test ]
fn lsp_code_actions_deno_cache_npm ( ) {
let mut session = TestSession ::from_file ( " initialize_params.json " ) ;
let diagnostics = session . did_open ( json! ( {
2022-11-29 19:32:18 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import chalk from \" npm:chalk \" ; \n \n console.log(chalk.green); \n "
}
} ) ) ;
2022-11-25 20:29:56 -05:00
assert_eq! (
diagnostics . with_source ( " deno " ) ,
load_fixture_as ( " code_actions/cache_npm/diagnostics.json " )
) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_actions/cache_npm/cache_action.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_actions/cache_npm/cache_response.json " ) )
) ;
session . shutdown_and_exit ( ) ;
}
#[ test ]
fn lsp_code_actions_imports ( ) {
let mut session = TestSession ::from_file ( " initialize_params.json " ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file00.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const abc = \" abc \" ; \n export const def = \" def \" ; \n "
}
} ) ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " \n console.log(abc); \n console.log(def) \n "
}
} ) ) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_params_imports.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_response_imports.json " ) )
) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" codeAction/resolve " ,
load_fixture ( " code_action_resolve_params_imports.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_resolve_response_imports.json " ) )
) ;
session . shutdown_and_exit ( ) ;
}
#[ test ]
fn lsp_code_actions_refactor ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " var x: { a?: number; b?: string } = {}; \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_params_refactor.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_response_refactor.json " ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" codeAction/resolve " ,
load_fixture ( " code_action_resolve_params_refactor.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_resolve_response_refactor.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_actions_refactor_no_disabled_support ( ) {
let mut client = init ( " initialize_params_ca_no_disabled.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " interface A { \n a: string; \n } \n \n interface B { \n b: string; \n } \n \n class AB implements A, B { \n a = \" a \" ; \n b = \" b \" ; \n } \n \n new AB().a; \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 14 ,
" character " : 0
}
} ,
" context " : {
" diagnostics " : [ ] ,
" only " : [
" refactor "
]
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_response_no_disabled.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_actions_deadlock ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
load_fixture ( " did_open_params_large.json " ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response ( id , json! ( [ { " enable " : true } ] ) )
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
read_diagnostics ( & mut client ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 444 ,
" character " : 11
} ,
" end " : {
" line " : 444 ,
" character " : 14
}
} ,
" text " : " +++ "
}
]
} ) ,
)
. unwrap ( ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 445 ,
" character " : 4
} ,
" end " : {
" line " : 445 ,
" character " : 4
}
} ,
" text " : " // "
}
]
} ) ,
)
. unwrap ( ) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 477 ,
" character " : 4
} ,
" end " : {
" line " : 477 ,
" character " : 9
}
} ,
" text " : " error "
}
]
} ) ,
)
. unwrap ( ) ;
// diagnostics only trigger after changes have elapsed in a separate thread,
// so we need to delay the next messages a little bit to attempt to create a
// potential for a deadlock with the codeAction
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 50 ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 609 ,
" character " : 33 ,
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/codeAction " ,
load_fixture ( " code_action_params_deadlock.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
read_diagnostics ( & mut client ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_completions ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " Deno. "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 5
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert! ( list . items . len ( ) > 90 ) ;
} else {
panic! ( " unexpected response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
load_fixture ( " completion_resolve_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " completion_resolve_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_completions_optional ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " interface A { \n b?: string; \n } \n \n const o: A = {}; \n \n function c(s: string) {} \n \n c(o.) "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
load_fixture ( " completion_request_params_optional.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " b? " ,
" kind " : 5 ,
" sortText " : " 11 " ,
" filterText " : " b " ,
" insertText " : " b " ,
" commitCharacters " : [ " . " , " , " , " ; " , " ( " ] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 79 ,
" name " : " b " ,
" useCodeSnippet " : false
}
}
}
]
} ) )
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
load_fixture ( " completion_resolve_params_optional.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" label " : " b? " ,
" kind " : 5 ,
" detail " : " (property) A.b?: string | undefined " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 1 " ,
" filterText " : " b " ,
" insertText " : " b "
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_completions_auto_import ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const foo = \" foo \" ; \n " ,
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export {}; \n \n " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 2 ,
" character " : 0 ,
} ,
" context " : {
" triggerKind " : 1 ,
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
if ! list . items . iter ( ) . any ( | item | item . label = = " foo " ) {
panic! ( " completions items missing 'foo' symbol " ) ;
}
} else {
panic! ( " unexpected completion response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
json! ( {
" label " : " foo " ,
" kind " : 6 ,
" sortText " : " 16 " ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 12 ,
" name " : " foo " ,
" source " : " ./b " ,
" data " : {
" exportName " : " foo " ,
" moduleSpecifier " : " ./b " ,
" fileName " : " file:///a/b.ts "
} ,
" useCodeSnippet " : false
}
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" label " : " foo " ,
" kind " : 6 ,
" detail " : " const foo: \" foo \" " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 16 " ,
" additionalTextEdits " : [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 0
} ,
" end " : {
" line " : 0 ,
" character " : 0
}
} ,
" newText " : " import { foo } from \" ./b.ts \" ; \n \n "
}
] ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
]
} ) )
) ;
}
#[ test ]
fn lsp_completions_snippet ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/a.tsx " ,
" languageId " : " typescriptreact " ,
" version " : 1 ,
" text " : " function A({ type }: { type: string }) { \n return type; \n } \n \n function B() { \n return <A t \n } " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/a.tsx "
} ,
" position " : {
" line " : 5 ,
" character " : 13 ,
} ,
" context " : {
" triggerKind " : 1 ,
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! (
json! ( list ) ,
json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " type " ,
" kind " : 5 ,
" sortText " : " 11 " ,
" filterText " : " type= \" $1 \" " ,
" insertText " : " type= \" $1 \" " ,
" insertTextFormat " : 2 ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/a.tsx " ,
" position " : 87 ,
" name " : " type " ,
" useCodeSnippet " : false
}
}
}
]
} )
) ;
} else {
panic! ( " unexpected completion response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
json! ( {
" label " : " type " ,
" kind " : 5 ,
" sortText " : " 11 " ,
" filterText " : " type= \" $1 \" " ,
" insertText " : " type= \" $1 \" " ,
" insertTextFormat " : 2 ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/a.tsx " ,
" position " : 87 ,
" name " : " type " ,
" useCodeSnippet " : false
}
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" label " : " type " ,
" kind " : 5 ,
" detail " : " (property) type: string " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 11 " ,
" filterText " : " type= \" $1 \" " ,
" insertText " : " type= \" $1 \" " ,
" insertTextFormat " : 2 ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
]
} ) )
) ;
}
#[ test ]
fn lsp_completions_no_snippet ( ) {
let mut client = init ( " initialize_params_no_snippet.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/a.tsx " ,
" languageId " : " typescriptreact " ,
" version " : 1 ,
" text " : " function A({ type }: { type: string }) { \n return type; \n } \n \n function B() { \n return <A t \n } " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/a.tsx "
} ,
" position " : {
" line " : 5 ,
" character " : 13 ,
} ,
" context " : {
" triggerKind " : 1 ,
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! (
json! ( list ) ,
json! ( {
" isIncomplete " : false ,
" items " : [
{
" label " : " type " ,
" kind " : 5 ,
" sortText " : " 11 " ,
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/a.tsx " ,
" position " : 87 ,
" name " : " type " ,
" useCodeSnippet " : false
}
}
}
]
} )
) ;
} else {
panic! ( " unexpected completion response " ) ;
}
}
#[ test ]
fn lsp_completions_npm ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import cjsDefault from 'npm:@denotest/cjs-default-export';import chalk from 'npm:chalk'; \n \n " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [
{
" uri " : " npm:@denotest/cjs-default-export " ,
} ,
{
" uri " : " npm:chalk " ,
}
]
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
// check importing a cjs default import
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 2 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 0
}
} ,
" text " : " cjsDefault. "
}
]
} ) ,
)
. unwrap ( ) ;
read_diagnostics ( & mut client ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 2 ,
" character " : 11
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 3 ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " default " ) ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " MyClass " ) ) ;
} else {
panic! ( " unexpected response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
load_fixture ( " completions/npm/resolve_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " completions/npm/resolve_response.json " ) )
) ;
// now check chalk, which is esm
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 3
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 2 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 11
}
} ,
" text " : " chalk. "
}
]
} ) ,
)
. unwrap ( ) ;
read_diagnostics ( & mut client ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 2 ,
" character " : 6
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " green " ) ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " red " ) ) ;
} else {
panic! ( " unexpected response " ) ;
}
shutdown ( & mut client ) ;
}
2022-11-29 19:32:18 -05:00
#[ test ]
fn lsp_npm_specifier_unopened_file ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
// create other.ts, which re-exports an npm specifier
client . deno_dir ( ) . write (
" other.ts " ,
" export { default as chalk } from 'npm:chalk@5'; " ,
) ;
// cache the other.ts file to the DENO_DIR
let deno = deno_cmd_with_deno_dir ( client . deno_dir ( ) )
. current_dir ( client . deno_dir ( ) . path ( ) )
. arg ( " cache " )
. arg ( " --quiet " )
. arg ( " other.ts " )
. envs ( env_vars_for_npm_tests ( ) )
. stdout ( Stdio ::piped ( ) )
. stderr ( Stdio ::piped ( ) )
. spawn ( )
. unwrap ( ) ;
let output = deno . wait_with_output ( ) . unwrap ( ) ;
assert! ( output . status . success ( ) ) ;
assert_eq! ( output . status . code ( ) , Some ( 0 ) ) ;
let stdout = String ::from_utf8 ( output . stdout ) . unwrap ( ) ;
assert! ( stdout . is_empty ( ) ) ;
let stderr = String ::from_utf8 ( output . stderr ) . unwrap ( ) ;
assert! ( stderr . is_empty ( ) ) ;
// open main.ts, which imports other.ts (unopened)
let main_url =
ModuleSpecifier ::from_file_path ( client . deno_dir ( ) . path ( ) . join ( " main.ts " ) )
. unwrap ( ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : main_url ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { chalk } from './other.ts'; \n \n " ,
}
} ) ,
) ;
client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : main_url ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 2 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 0
}
} ,
" text " : " chalk. "
}
]
} ) ,
)
. unwrap ( ) ;
read_diagnostics ( & mut client ) ;
// now ensure completions work
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : main_url
} ,
" position " : {
" line " : 2 ,
" character " : 6
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 63 ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " ansi256 " ) ) ;
} else {
panic! ( " unexpected response " ) ;
}
}
2022-11-25 20:29:56 -05:00
#[ test ]
fn lsp_completions_registry ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params_registry.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 46
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 3 ) ;
} else {
panic! ( " unexpected response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
load_fixture ( " completion_resolve_params_registry.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " completion_resolve_response_registry.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_completions_registry_empty ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params_registry.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" \" "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 20
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " \" "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " completion_request_response_empty.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_auto_discover_registry ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 46
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( method , maybe_res ) = client . read_notification ( ) . unwrap ( ) ;
assert_eq! ( method , " deno/registryState " ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" origin " : " http://localhost:4545 " ,
" suggestions " : true ,
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_cache_location ( ) {
let _g = http_server ( ) ;
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params_registry.json " ) )
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " cache " . to_string ( ) , json! ( " .cache " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let mut session = TestSession ::from_client ( client ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ) ;
let diagnostics =
session . did_open ( load_fixture ( " did_open_params_import_hover.json " ) ) ;
assert_eq! ( diagnostics . viewed ( ) . len ( ) , 7 ) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [ ] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 0 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js \n \n **Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts \n "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 19
} ,
" end " :{
" line " : 0 ,
" character " : 62
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 7 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://localhost:4545/x/a/mod.ts \n \n \n --- \n \n **a** \n \n mod.ts "
} ,
" range " : {
" start " : {
" line " : 7 ,
" character " : 19
} ,
" end " : {
" line " : 7 ,
" character " : 53
}
}
} ) )
) ;
let cache_path = temp_dir . path ( ) . join ( " .cache " ) ;
assert! ( cache_path . is_dir ( ) ) ;
assert! ( cache_path . join ( " gen " ) . is_dir ( ) ) ;
session . shutdown_and_exit ( ) ;
}
/// Sets the TLS root certificate on startup, which allows the LSP to connect to
/// the custom signed test server and be able to retrieve the registry config
/// and cache files.
#[ test ]
fn lsp_tls_cert ( ) {
let _g = http_server ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params_tls_cert.json " ) )
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( testdata_path ( ) ) . unwrap ( ) ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
let mut session = TestSession ::from_client ( client ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ) ;
let diagnostics =
session . did_open ( load_fixture ( " did_open_params_tls_cert.json " ) ) ;
let diagnostics = diagnostics . viewed ( ) ;
assert_eq! ( diagnostics . len ( ) , 7 ) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [ ] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 0 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: https​://localhost:5545/xTypeScriptTypes.js \n "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 19
} ,
" end " :{
" line " : 0 ,
" character " : 63
}
}
} ) )
) ;
let ( maybe_res , maybe_err ) = session
. client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : {
" line " : 7 ,
" character " : 28
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://localhost:4545/x/a/mod.ts \n \n \n --- \n \n **a** \n \n mod.ts "
} ,
" range " : {
" start " : {
" line " : 7 ,
" character " : 19
} ,
" end " : {
" line " : 7 ,
" character " : 53
}
}
} ) )
) ;
session . shutdown_and_exit ( ) ;
}
#[ test ]
fn lsp_diagnostics_warn_redirect ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://127.0.0.1:4545/x_deno_warning.js \" ; \n \n console.log(a) \n " ,
} ,
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [
{
" uri " : " http://127.0.0.1:4545/x_deno_warning.js " ,
}
] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let diagnostics = read_diagnostics ( & mut client ) ;
assert_eq! (
diagnostics . with_source ( " deno " ) ,
lsp ::PublishDiagnosticsParams {
uri : Url ::parse ( " file:///a/file.ts " ) . unwrap ( ) ,
diagnostics : vec ! [
lsp ::Diagnostic {
range : lsp ::Range {
start : lsp ::Position {
line : 0 ,
character : 19
} ,
end : lsp ::Position {
line : 0 ,
character : 60
}
} ,
severity : Some ( lsp ::DiagnosticSeverity ::WARNING ) ,
code : Some ( lsp ::NumberOrString ::String ( " deno-warn " . to_string ( ) ) ) ,
source : Some ( " deno " . to_string ( ) ) ,
message : " foobar " . to_string ( ) ,
.. Default ::default ( )
} ,
lsp ::Diagnostic {
range : lsp ::Range {
start : lsp ::Position {
line : 0 ,
character : 19
} ,
end : lsp ::Position {
line : 0 ,
character : 60
}
} ,
severity : Some ( lsp ::DiagnosticSeverity ::INFORMATION ) ,
code : Some ( lsp ::NumberOrString ::String ( " redirect " . to_string ( ) ) ) ,
source : Some ( " deno " . to_string ( ) ) ,
message : " The import of \" http://127.0.0.1:4545/x_deno_warning.js \" was redirected to \" http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js \" . " . to_string ( ) ,
data : Some ( json! ( { " specifier " : " http://127.0.0.1:4545/x_deno_warning.js " , " redirect " : " http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js " } ) ) ,
.. Default ::default ( )
}
] ,
version : Some ( 1 ) ,
}
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_redirect_quick_fix ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://127.0.0.1:4545/x_deno_warning.js \" ; \n \n console.log(a) \n " ,
} ,
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.ts " ,
} ,
" uris " : [
{
" uri " : " http://127.0.0.1:4545/x_deno_warning.js " ,
}
] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let diagnostics = read_diagnostics ( & mut client )
. with_source ( " deno " )
. diagnostics ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 19
} ,
" end " : {
" line " : 0 ,
" character " : 60
}
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [
" quickfix "
]
}
} ) ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_redirect_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_diagnostics_deprecated ( ) {
let mut client = init ( " initialize_params.json " ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /** @deprecated */ \n export const a = \" a \" ; \n \n a; \n " ,
} ,
} ) ,
) ;
assert_eq! (
json! ( diagnostics ) ,
json! ( [
{
" uri " : " file:///a/file.ts " ,
" diagnostics " : [ ] ,
" version " : 1
} ,
{
" uri " : " file:///a/file.ts " ,
" diagnostics " : [ ] ,
" version " : 1
} ,
{
" uri " : " file:///a/file.ts " ,
" diagnostics " : [
{
" range " : {
" start " : {
" line " : 3 ,
" character " : 0
} ,
" end " : {
" line " : 3 ,
" character " : 1
}
} ,
" severity " : 4 ,
" code " : 6385 ,
" source " : " deno-ts " ,
" message " : " 'a' is deprecated. " ,
" relatedInformation " : [ ] ,
" tags " : [
2
]
}
] ,
" version " : 1
}
] )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_diagnostics_deno_types ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
load_fixture ( " did_open_params_deno_types.json " ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response ( id , json! ( [ { " enable " : true } ] ) )
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/documentSymbol " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_res . is_some ( ) ) ;
assert! ( maybe_err . is_none ( ) ) ;
let diagnostics = read_diagnostics ( & mut client ) ;
assert_eq! ( diagnostics . viewed ( ) . len ( ) , 5 ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_diagnostics_refresh_dependents ( ) {
let mut session = TestSession ::from_file ( " initialize_params.json " ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_00.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
} ,
} ) ) ;
session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export * from \" ./file_00.ts \" ; \n " ,
} ,
} ) ) ;
let diagnostics = session . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_02.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { a, b } from \" ./file_01.ts \" ; \n \n console.log(a, b); \n "
}
} ) ) ;
assert_eq! (
json! ( diagnostics . with_file_and_source ( " file:///a/file_02.ts " , " deno-ts " ) ) ,
json! ( {
" uri " : " file:///a/file_02.ts " ,
" diagnostics " : [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 12
} ,
" end " : {
" line " : 0 ,
" character " : 13
}
} ,
" severity " : 1 ,
" code " : 2305 ,
" source " : " deno-ts " ,
" message " : " Module ' \" ./file_01.ts \" ' has no exported member 'b'. "
}
] ,
" version " : 1
} )
) ;
// fix the code causing the diagnostic
session
. client
. write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file_00.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 0
} ,
" end " : {
" line " : 1 ,
" character " : 0
}
} ,
" text " : " export const b = \" b \" ; \n "
}
]
} ) ,
)
. unwrap ( ) ;
let diagnostics = session . read_diagnostics ( ) ;
assert_eq! ( diagnostics . viewed ( ) . len ( ) , 0 ) ; // no diagnostics now
session . shutdown_and_exit ( ) ;
assert_eq! ( session . client . queue_len ( ) , 0 ) ;
}
#[ derive(Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct PerformanceAverage {
pub name : String ,
pub count : u32 ,
pub average_duration : u32 ,
}
#[ derive(Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct PerformanceAverages {
averages : Vec < PerformanceAverage > ,
}
#[ test ]
fn lsp_performance ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 19
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , PerformanceAverages > (
" deno/performance " ,
json! ( null ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( res ) = maybe_res {
assert_eq! ( res . averages . len ( ) , 13 ) ;
} else {
panic! ( " unexpected result " ) ;
}
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_no_changes ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console; \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
client . assert_no_notification ( " window/showMessage " ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_error ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console test test \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_mbc ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const bar = '👍🇺🇸😃' \n console.log('hello deno') \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( load_fixture ( " formatting_mbc_response.json " ) ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_exclude_with_config ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_fmt_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.fmt.exclude.jsonc " ) )
. unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " deno.fmt.jsonc " ) , deno_fmt_jsonc ) . unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./deno.fmt.jsonc " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
let file_uri =
ModuleSpecifier ::from_file_path ( temp_dir . path ( ) . join ( " ignored.ts " ) )
. unwrap ( )
. to_string ( ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " function myFunc(){} "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : file_uri
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_exclude_default_config ( ) {
let temp_dir = TempDir ::new ( ) ;
let workspace_root = temp_dir . path ( ) . canonicalize ( ) . unwrap ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.fmt.exclude.jsonc " ) )
. unwrap ( ) ;
fs ::write ( workspace_root . join ( " deno.jsonc " ) , deno_jsonc ) . unwrap ( ) ;
params . root_uri =
Some ( Url ::from_file_path ( workspace_root . clone ( ) ) . unwrap ( ) ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
let file_uri =
ModuleSpecifier ::from_file_path ( workspace_root . join ( " ignored.ts " ) )
. unwrap ( )
. to_string ( ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " function myFunc(){} "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : file_uri
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_json ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" key \" : \" value \" } "
}
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 1
} ,
" end " : {
" line " : 0 ,
" character " : 1
}
} ,
" newText " : " "
} ,
{
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" newText " : " "
} ,
{
" range " : {
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 15 }
} ,
" newText " : " } \n "
}
] ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_json_no_diagnostics ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" key \" : \" value \" } "
}
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json "
} ,
" position " : {
" line " : 0 ,
" character " : 3
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_markdown ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md " ,
" languageId " : " markdown " ,
" version " : 1 ,
" text " : " # Hello World "
}
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [
{
" range " : {
" start " : { " line " : 0 , " character " : 1 } ,
" end " : { " line " : 0 , " character " : 3 }
} ,
" newText " : " "
} ,
{
" range " : {
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 15 }
} ,
" newText " : " \n "
}
] ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_format_with_config ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_fmt_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.fmt.jsonc " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " deno.fmt.jsonc " ) , deno_fmt_jsonc ) . unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./deno.fmt.jsonc " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export async function someVeryLongFunctionName() { \n const response = fetch( \" http://localhost:4545/some/non/existent/path.json \" ); \n console.log(response.text()); \n console.log( \" finished! \" ) \n } "
}
} ) ,
)
. unwrap ( ) ;
// The options below should be ignored in favor of configuration from config file.
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [ {
" range " : {
" start " : {
" line " : 1 ,
" character " : 0
} ,
" end " : {
" line " : 1 ,
" character " : 0
}
} ,
" newText " : " \t "
} ,
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 23
} ,
" end " : {
" line " : 1 ,
" character " : 24
}
} ,
" newText " : " \n \t \t ' "
} ,
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 73
} ,
" end " : {
" line " : 1 ,
" character " : 74
}
} ,
" newText " : " ', \n \t "
} ,
{
" range " : {
" start " : {
" line " : 2 ,
" character " : 0
} ,
" end " : {
" line " : 2 ,
" character " : 0
}
} ,
" newText " : " \t "
} ,
{
" range " : {
" start " : {
" line " : 3 ,
" character " : 0
} ,
" end " : {
" line " : 3 ,
" character " : 0
}
} ,
" newText " : " \t "
} ,
{
" range " : {
" start " : {
" line " : 3 ,
" character " : 12
} ,
" end " : {
" line " : 3 ,
" character " : 13
}
} ,
" newText " : " ' "
} ,
{
" range " : {
" start " : {
" line " : 3 ,
" character " : 22
} ,
" end " : {
" line " : 3 ,
" character " : 24
}
} ,
" newText " : " '); "
} ,
{
" range " : {
" start " : {
" line " : 4 ,
" character " : 1
} ,
" end " : {
" line " : 4 ,
" character " : 1
}
} ,
" newText " : " \n "
} ]
) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_markdown_no_diagnostics ( ) {
let mut client = init ( " initialize_params.json " ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md " ,
" languageId " : " markdown " ,
" version " : 1 ,
" text " : " # Hello World "
}
} ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
} ,
" position " : {
" line " : 0 ,
" character " : 3
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! ( maybe_res , Some ( json! ( null ) ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_configuration_did_change ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params_did_config_change.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ,
) ;
client
. write_notification (
" workspace/didChangeConfiguration " ,
json! ( {
" settings " : { }
} ) ,
)
. unwrap ( ) ;
let ( id , method , _ ) = client . read_request ::< Value > ( ) . unwrap ( ) ;
assert_eq! ( method , " workspace/configuration " ) ;
client
. write_response (
id ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" implementations " : true ,
" references " : true
} ,
" importMap " : null ,
" lint " : true ,
" suggest " : {
" autoImports " : true ,
" completeFunctionCalls " : false ,
" names " : true ,
" paths " : true ,
" imports " : {
" hosts " : {
" http://localhost:4545/ " : true
}
}
} ,
" unstable " : false
} ] ) ,
)
. unwrap ( ) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/completion " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : {
" line " : 0 ,
" character " : 46
} ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
if let Some ( lsp ::CompletionResponse ::List ( list ) ) = maybe_res {
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 3 ) ;
} else {
panic! ( " unexpected response " ) ;
}
let ( maybe_res , maybe_err ) = client
. write_request (
" completionItem/resolve " ,
load_fixture ( " completion_resolve_params_registry.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " completion_resolve_response_registry.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_workspace_symbol ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export class A { \n fieldA: string; \n fieldB: string; \n } \n " ,
}
} ) ,
) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export class B { \n fieldC: string; \n fieldD: string; \n } \n " ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" workspace/symbol " ,
json! ( {
" query " : " field "
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( [
{
" name " : " fieldA " ,
" kind " : 8 ,
" location " : {
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 1 ,
" character " : 17
}
}
} ,
" containerName " : " A "
} ,
{
" name " : " fieldB " ,
" kind " : 8 ,
" location " : {
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 2 ,
" character " : 2
} ,
" end " : {
" line " : 2 ,
" character " : 17
}
}
} ,
" containerName " : " A "
} ,
{
" name " : " fieldC " ,
" kind " : 8 ,
" location " : {
" uri " : " file:///a/file_01.ts " ,
" range " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 1 ,
" character " : 17
}
}
} ,
" containerName " : " B "
} ,
{
" name " : " fieldD " ,
" kind " : 8 ,
" location " : {
" uri " : " file:///a/file_01.ts " ,
" range " : {
" start " : {
" line " : 2 ,
" character " : 2
} ,
" end " : {
" line " : 2 ,
" character " : 17
}
}
} ,
" containerName " : " B "
}
] ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_code_actions_ignore_lint ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " let message = 'Hello, Deno!'; \n console.log(message); \n "
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_ignore_lint_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_ignore_lint_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
/// This test exercises updating an existing deno-lint-ignore-file comment.
#[ test ]
fn lsp_code_actions_update_ignore_lint ( ) {
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " :
" #!/usr/bin/env -S deno run
// deno-lint-ignore-file camelcase
let snake_case = ' Hello , Deno ! ' ;
console . log ( snake_case ) ;
" ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request (
" textDocument/codeAction " ,
load_fixture ( " code_action_update_ignore_lint_params.json " ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( load_fixture ( " code_action_update_ignore_lint_response.json " ) )
) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_lint_with_config ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_lint_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.lint.jsonc " ) ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " deno.lint.jsonc " ) , deno_lint_jsonc )
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./deno.lint.jsonc " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
let mut session = TestSession ::from_client ( client ) ;
let diagnostics = session . did_open ( load_fixture ( " did_open_lint.json " ) ) ;
let diagnostics = diagnostics . viewed ( ) ;
assert_eq! ( diagnostics . len ( ) , 1 ) ;
assert_eq! (
diagnostics [ 0 ] . code ,
Some ( lsp ::NumberOrString ::String ( " ban-untagged-todo " . to_string ( ) ) )
) ;
session . shutdown_and_exit ( ) ;
}
#[ test ]
fn lsp_lint_exclude_with_config ( ) {
let temp_dir = TempDir ::new ( ) ;
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let deno_lint_jsonc =
serde_json ::to_vec_pretty ( & load_fixture ( " deno.lint.exclude.jsonc " ) )
. unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " deno.lint.jsonc " ) , deno_lint_jsonc )
. unwrap ( ) ;
params . root_uri = Some ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
if let Some ( Value ::Object ( mut map ) ) = params . initialization_options {
map . insert ( " config " . to_string ( ) , json! ( " ./deno.lint.jsonc " ) ) ;
params . initialization_options = Some ( Value ::Object ( map ) ) ;
}
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
let diagnostics = did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : ModuleSpecifier ::from_file_path ( temp_dir . path ( ) . join ( " ignored.ts " ) ) . unwrap ( ) . to_string ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " // TODO: fixme \n export async function non_camel_case() { \n console.log( \" finished! \" ) \n } "
}
} ) ,
) ;
let diagnostics = diagnostics
. into_iter ( )
. flat_map ( | x | x . diagnostics )
. collect ::< Vec < _ > > ( ) ;
assert_eq! ( diagnostics , Vec ::new ( ) ) ;
shutdown ( & mut client ) ;
}
#[ test ]
fn lsp_jsx_import_source_pragma ( ) {
let _g = http_server ( ) ;
let mut client = init ( " initialize_params.json " ) ;
did_open (
& mut client ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.tsx " ,
" languageId " : " typescriptreact " ,
" version " : 1 ,
" text " :
" /** @jsxImportSource http://localhost:4545/jsx */
function A ( ) {
return \ " hello \" ;
}
export function B ( ) {
return < A > < / A > ;
}
" ,
}
} ) ,
) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" deno/cache " ,
json! ( {
" referrer " : {
" uri " : " file:///a/file.tsx " ,
} ,
" uris " : [
{
" uri " : " http://127.0.0.1:4545/jsx/jsx-runtime " ,
}
] ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , Value > (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.tsx "
} ,
" position " : {
" line " : 0 ,
" character " : 25
}
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert_eq! (
maybe_res ,
Some ( json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://localhost:4545/jsx/jsx-runtime \n " ,
} ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 21
} ,
" end " : {
" line " : 0 ,
" character " : 46
}
}
} ) )
) ;
shutdown ( & mut client ) ;
}
#[ derive(Debug, Clone, Deserialize, PartialEq) ]
#[ serde(rename_all = " camelCase " ) ]
struct TestData {
id : String ,
label : String ,
steps : Option < Vec < TestData > > ,
range : Option < lsp ::Range > ,
}
#[ derive(Debug, Deserialize, PartialEq) ]
#[ serde(rename_all = " camelCase " ) ]
enum TestModuleNotificationKind {
Insert ,
Replace ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct TestModuleNotificationParams {
text_document : lsp ::TextDocumentIdentifier ,
kind : TestModuleNotificationKind ,
label : String ,
tests : Vec < TestData > ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct EnqueuedTestModule {
text_document : lsp ::TextDocumentIdentifier ,
ids : Vec < String > ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct TestRunResponseParams {
enqueued : Vec < EnqueuedTestModule > ,
}
#[ test ]
fn lsp_testing_api ( ) {
let mut params : lsp ::InitializeParams =
serde_json ::from_value ( load_fixture ( " initialize_params.json " ) ) . unwrap ( ) ;
let temp_dir = TempDir ::new ( ) ;
let root_specifier =
ensure_directory_specifier ( Url ::from_file_path ( temp_dir . path ( ) ) . unwrap ( ) ) ;
let module_path = temp_dir . path ( ) . join ( " ./test.ts " ) ;
let specifier = ModuleSpecifier ::from_file_path ( & module_path ) . unwrap ( ) ;
let contents = r #"
Deno . test ( {
name : " test a " ,
fn ( ) {
console . log ( " test a " ) ;
}
} ) ;
" #;
fs ::write ( & module_path , contents ) . unwrap ( ) ;
fs ::write ( temp_dir . path ( ) . join ( " ./deno.jsonc " ) , r # "{}"# ) . unwrap ( ) ;
params . root_uri = Some ( root_specifier ) ;
let deno_exe = deno_exe_path ( ) ;
let mut client = LspClient ::new ( & deno_exe , false ) . unwrap ( ) ;
client
. write_request ::< _ , _ , Value > ( " initialize " , params )
. unwrap ( ) ;
client . write_notification ( " initialized " , json! ( { } ) ) . unwrap ( ) ;
client
. write_notification (
" textDocument/didOpen " ,
json! ( {
" textDocument " : {
" uri " : specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : contents ,
}
} ) ,
)
. unwrap ( ) ;
handle_configuration_request (
& mut client ,
json! ( [ {
" enable " : true ,
" codeLens " : {
" test " : true
}
} ] ) ,
) ;
for _ in 0 .. 4 {
let result = client . read_notification ::< Value > ( ) ;
assert! ( result . is_ok ( ) ) ;
let ( method , notification ) = result . unwrap ( ) ;
if method . as_str ( ) = = " deno/testModule " {
let params : TestModuleNotificationParams =
serde_json ::from_value ( notification . unwrap ( ) ) . unwrap ( ) ;
assert_eq! ( params . text_document . uri , specifier ) ;
assert_eq! ( params . kind , TestModuleNotificationKind ::Replace ) ;
assert_eq! ( params . label , " test.ts " ) ;
assert_eq! ( params . tests . len ( ) , 1 ) ;
let test = & params . tests [ 0 ] ;
assert_eq! ( test . label , " test a " ) ;
assert! ( test . steps . is_none ( ) ) ;
assert_eq! (
test . range ,
Some ( lsp ::Range {
start : lsp ::Position {
line : 1 ,
character : 5 ,
} ,
end : lsp ::Position {
line : 1 ,
character : 9 ,
}
} )
) ;
}
}
let ( maybe_res , maybe_err ) = client
. write_request ::< _ , _ , TestRunResponseParams > (
" deno/testRun " ,
json! ( {
" id " : 1 ,
" kind " : " run " ,
} ) ,
)
. unwrap ( ) ;
assert! ( maybe_err . is_none ( ) ) ;
assert! ( maybe_res . is_some ( ) ) ;
let res = maybe_res . unwrap ( ) ;
assert_eq! ( res . enqueued . len ( ) , 1 ) ;
assert_eq! ( res . enqueued [ 0 ] . text_document . uri , specifier ) ;
assert_eq! ( res . enqueued [ 0 ] . ids . len ( ) , 1 ) ;
let id = res . enqueued [ 0 ] . ids [ 0 ] . clone ( ) ;
let res = client . read_notification ::< Value > ( ) ;
assert! ( res . is_ok ( ) ) ;
let ( method , notification ) = res . unwrap ( ) ;
assert_eq! ( method , " deno/testRunProgress " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" id " : 1 ,
" message " : {
" type " : " started " ,
" test " : {
" textDocument " : {
" uri " : specifier ,
} ,
" id " : id ,
} ,
}
} ) )
) ;
let res = client . read_notification ::< Value > ( ) ;
assert! ( res . is_ok ( ) ) ;
let ( method , notification ) = res . unwrap ( ) ;
assert_eq! ( method , " deno/testRunProgress " ) ;
let notification_value = notification
. as_ref ( )
. unwrap ( )
. as_object ( )
. unwrap ( )
. get ( " message " )
. unwrap ( )
. as_object ( )
. unwrap ( )
. get ( " value " )
. unwrap ( )
. as_str ( )
. unwrap ( ) ;
// deno test's output capturing flushes with a zero-width space in order to
// synchronize the output pipes. Occassionally this zero width space
// might end up in the output so strip it from the output comparison here.
assert_eq! ( notification_value . replace ( '\u{200B}' , " " ) , " test a \r \n " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" id " : 1 ,
" message " : {
" type " : " output " ,
" value " : notification_value ,
" test " : {
" textDocument " : {
" uri " : specifier ,
} ,
" id " : id ,
} ,
}
} ) )
) ;
let res = client . read_notification ::< Value > ( ) ;
assert! ( res . is_ok ( ) ) ;
let ( method , notification ) = res . unwrap ( ) ;
assert_eq! ( method , " deno/testRunProgress " ) ;
let notification = notification . unwrap ( ) ;
let obj = notification . as_object ( ) . unwrap ( ) ;
assert_eq! ( obj . get ( " id " ) , Some ( & json! ( 1 ) ) ) ;
let message = obj . get ( " message " ) . unwrap ( ) . as_object ( ) . unwrap ( ) ;
match message . get ( " type " ) . and_then ( | v | v . as_str ( ) ) {
Some ( " passed " ) = > {
assert_eq! (
message . get ( " test " ) ,
Some ( & json! ( {
" textDocument " : {
" uri " : specifier
} ,
" id " : id ,
} ) )
) ;
assert! ( message . contains_key ( " duration " ) ) ;
let res = client . read_notification ::< Value > ( ) ;
assert! ( res . is_ok ( ) ) ;
let ( method , notification ) = res . unwrap ( ) ;
assert_eq! ( method , " deno/testRunProgress " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" id " : 1 ,
" message " : {
" type " : " end " ,
}
} ) )
) ;
}
// sometimes on windows, the messages come out of order, but it actually is
// working, so if we do get the end before the passed, we will simply let
// the test pass
Some ( " end " ) = > ( ) ,
_ = > panic! ( " unexpected message {} " , json! ( notification ) ) ,
}
shutdown ( & mut client ) ;
}
}