2023-01-12 20:59:13 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_ast ::ModuleSpecifier ;
use deno_core ::serde ::Deserialize ;
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 ::fs ;
2023-05-22 21:28:36 -04:00
use test_util ::assert_starts_with ;
2023-01-12 20:59:13 -05:00
use test_util ::deno_cmd_with_deno_dir ;
use test_util ::env_vars_for_npm_tests ;
2023-05-22 21:28:36 -04:00
use test_util ::lsp ::LspClient ;
2023-01-12 20:59:13 -05:00
use test_util ::testdata_path ;
2023-03-08 18:15:20 -05:00
use test_util ::TestContextBuilder ;
2023-01-12 20:59:13 -05:00
use tower_lsp ::lsp_types as lsp ;
#[ test ]
fn lsp_startup_shutdown ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_init_tsconfig ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
temp_dir . write (
" lib.tsconfig.json " ,
r #" {
" compilerOptions " : {
" lib " : [ " deno.ns " , " deno.unstable " , " dom " ]
}
} " #,
) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " lib.tsconfig.json " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " location.pathname; \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_tsconfig_types ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
temp_dir . write (
" types.tsconfig.json " ,
r #" {
" compilerOptions " : {
" types " : [ " ./a.d.ts " ]
} ,
" lint " : {
" rules " : {
" tags " : [ ]
}
}
} " #,
) ;
let a_dts = " // deno-lint-ignore-file no-var \n declare var a: string; " ;
temp_dir . write ( " a.d.ts " , a_dts ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " types.tsconfig.json " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : Url ::from_file_path ( temp_dir . path ( ) . join ( " test.ts " ) ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(a); \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_tsconfig_bad_config_path ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder
. set_config ( " bad_tsconfig.json " )
. set_maybe_root_uri ( None ) ;
} ) ;
2023-03-09 15:09:03 -05:00
let ( method , maybe_params ) = client . read_notification ( ) ;
2023-01-12 20:59:13 -05:00
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 ( )
} ) ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_triple_slash_types ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
let a_dts = " // deno-lint-ignore-file no-var \n declare var a: string; " ;
temp_dir . write ( " a.d.ts " , a_dts ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " test.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /// <reference types= \" ./a.d.ts \" /> \n \n console.log(a); \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_import_map ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
let import_map = r #" {
" imports " : {
" /~/ " : " ./lib/ "
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
} " #;
temp_dir . write ( " import-map.json " , import_map ) ;
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " import-map.json " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
let uri = Url ::from_file_path ( temp_dir . path ( ) . join ( " a.ts " ) ) . unwrap ( ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-09-24 13:00:15 -04:00
#[ test ]
fn lsp_import_map_remote ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write (
" deno.json " ,
json! ( {
" importMap " : " http://localhost:4545/import_maps/import_map.json " ,
} )
. to_string ( ) ,
) ;
temp_dir . write (
" file.ts " ,
r #"
import { printHello } from " print_hello " ;
printHello ( ) ;
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " http://localhost:4545/import_maps/import_map.json " ) ;
} ) ;
client . write_request (
" workspace/executeCommand " ,
json! ( {
" command " : " deno.cache " ,
2023-09-26 16:57:14 -04:00
" arguments " : [ [ ] , temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ] ,
2023-09-24 13:00:15 -04:00
} ) ,
) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " file.ts " ) ,
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) , vec! [ ] ) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_import_map_data_url ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " data:application/json;utf8,{ \" imports \" : { \" example \" : \" https://deno.land/x/example/mod.ts \" }} " ) ;
} ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import example from \" example \" ; \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
// This indicates that the import map is applied correctly.
2023-05-26 02:10:18 -04:00
assert! ( diagnostics . all ( ) . iter ( ) . any ( | diagnostic | diagnostic . code
2023-01-12 20:59:13 -05:00
= = Some ( lsp ::NumberOrString ::String ( " no-cache " . to_string ( ) ) )
& & diagnostic
. message
. contains ( " https://deno.land/x/example/mod.ts " ) ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_import_map_config_file ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.import_map.jsonc " ,
r #" {
" importMap " : " import-map.json "
} " #,
) ;
temp_dir . write (
" import-map.json " ,
r #" {
" imports " : {
" /~/ " : " ./lib/ "
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
} " #,
) ;
2023-01-28 10:18:32 -05:00
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.import_map.jsonc " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let uri = temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ;
2023-01-25 15:13:40 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
2023-01-25 15:13:40 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
2023-01-25 15:13:40 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-25 15:13:40 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
2023-01-25 15:13:40 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-25 15:13:40 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-25 15:13:40 -05:00
}
#[ test ]
fn lsp_import_map_embedded_in_config_file ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
2023-01-25 15:13:40 -05:00
" deno.embedded_import_map.jsonc " ,
2023-03-08 18:15:20 -05:00
r #" {
" imports " : {
" /~/ " : " ./lib/ "
2023-01-25 15:13:40 -05:00
}
2023-03-08 18:15:20 -05:00
} " #,
) ;
temp_dir . create_dir_all ( " lib " ) ;
2023-01-25 15:13:40 -05:00
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.embedded_import_map.jsonc " ) ;
} ) ;
2023-01-25 15:13:40 -05:00
2023-03-08 18:15:20 -05:00
let uri = temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-06-13 09:24:22 -04:00
#[ test ]
fn lsp_import_map_embedded_in_config_file_after_initialize ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " deno.embedded_import_map.jsonc " , " {} " ) ;
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.embedded_import_map.jsonc " ) ;
} ) ;
let uri = temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 1 ) ;
// update the import map
temp_dir . write (
" deno.embedded_import_map.jsonc " ,
r #" {
" imports " : {
" /~/ " : " ./lib/ "
}
} " #,
) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.embedded_import_map.jsonc " ) . unwrap ( ) ,
" type " : 2
} ]
} ) ) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) . len ( ) , 0 ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
}
} )
) ;
client . shutdown ( ) ;
}
2023-07-20 14:05:52 -04:00
#[ test ]
fn lsp_import_map_config_file_auto_discovered ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
let mut client = context . new_lsp_command ( ) . capture_stderr ( ) . build ( ) ;
client . initialize_default ( ) ;
// add the deno.json
temp_dir . write ( " deno.jsonc " , r # "{ "imports": { "/~/": "./lib/" } }"# ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.jsonc " ) . unwrap ( ) ,
" type " : 2
} ]
} ) ) ;
client . wait_until_stderr_line ( | line | {
line . contains ( " Auto-resolved configuration file: " )
} ) ;
let uri = temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
}
} )
) ;
// now cause a syntax error
temp_dir . write ( " deno.jsonc " , r # ",,#,#,,"# ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.jsonc " ) . unwrap ( ) ,
" type " : 2
} ]
} ) ) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) . len ( ) , 1 ) ;
// now fix it, and things should work again
temp_dir . write ( " deno.jsonc " , r # "{ "imports": { "/~/": "./lib/" } }"# ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.jsonc " ) . unwrap ( ) ,
" type " : 2
} ]
} ) ) ;
client . wait_until_stderr_line ( | line | {
line . contains ( " Auto-resolved configuration file: " )
} ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : { " line " : 2 , " character " : 12 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " :" (alias) const b: \" b \" \n import b "
} ,
" "
] ,
" range " : {
" start " : { " line " : 2 , " character " : 12 } ,
" end " : { " line " : 2 , " character " : 13 }
}
} )
) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) . len ( ) , 0 ) ;
client . shutdown ( ) ;
}
2023-07-21 09:12:26 -04:00
#[ test ]
fn lsp_import_map_config_file_auto_discovered_symlink ( ) {
let context = TestContextBuilder ::new ( )
// DO NOT COPY THIS CODE. Very rare case where we want to force the temp
// directory on the CI to not be a symlinked directory because we are
// testing a scenario with a symlink to a non-symlink in the same directory
// tree. Generally you want to ensure your code works in symlinked directories
// so don't use this unless you have a similar scenario.
. temp_dir_path ( std ::env ::temp_dir ( ) . canonicalize ( ) . unwrap ( ) )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
let mut client = context . new_lsp_command ( ) . capture_stderr ( ) . build ( ) ;
client . initialize_default ( ) ;
// now create a symlink in the current directory to a subdir/deno.json
// and ensure the watched files notification still works
temp_dir . create_dir_all ( " subdir " ) ;
temp_dir . write ( " subdir/deno.json " , r # "{ }"# ) ;
temp_dir . symlink_file (
temp_dir . path ( ) . join ( " subdir " ) . join ( " deno.json " ) ,
temp_dir . path ( ) . join ( " deno.json " ) ,
) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
// the client will give a watched file changed event for the symlink's target
2023-08-02 16:57:25 -04:00
" uri " : temp_dir . path ( ) . join ( " subdir/deno.json " ) . canonicalize ( ) . uri_file ( ) ,
2023-07-21 09:12:26 -04:00
" type " : 2
} ]
} ) ) ;
// this will discover the deno.json in the root
let search_line = format! (
" Auto-resolved configuration file: \" {} \" " ,
temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) . as_str ( )
) ;
client . wait_until_stderr_line ( | line | line . contains ( & search_line ) ) ;
// now open a file which will cause a diagnostic because the import map is empty
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { b } from \" /~/b.ts \" ; \n \n console.log(b); \n "
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 1 ) ;
// update the import map to have the imports now
temp_dir . write ( " subdir/deno.json " , r # "{ "imports": { "/~/": "./lib/" } }"# ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
// now still say that the target path has changed
2023-08-02 16:57:25 -04:00
" uri " : temp_dir . path ( ) . join ( " subdir/deno.json " ) . canonicalize ( ) . uri_file ( ) ,
2023-07-21 09:12:26 -04:00
" type " : 2
} ]
} ) ) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) . len ( ) , 0 ) ;
client . shutdown ( ) ;
}
2023-07-26 18:52:31 -04:00
#[ test ]
fn lsp_import_map_node_specifiers ( ) {
let context = TestContextBuilder ::for_npm ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " deno.json " , r # "{ "imports": { "fs": "node:fs" } }"# ) ;
// cache @types/node
context
. new_command ( )
. args ( " cache npm:@types/node " )
. run ( )
. skip_output_check ( )
. assert_exit_code ( 0 ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.json " ) ;
} ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import fs from \" fs \" ; \n console.log(fs); "
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) , vec! [ ] ) ;
client . shutdown ( ) ;
}
2023-10-09 18:43:32 -04:00
#[ test ]
fn lsp_format_vendor_path ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " deno.json " , json! ( { " vendor " : true } ) . to_string ( ) ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" import " http ://localhost:4545/run/002_hello.ts";"#,
} ,
} ) ) ;
client . write_request (
" workspace/executeCommand " ,
json! ( {
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
} ) ,
) ;
assert! ( temp_dir
. path ( )
. join ( " vendor/http_localhost_4545/run/002_hello.ts " )
. exists ( ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " vendor/http_localhost_4545/run/002_hello.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" console.log( " Hello World " ); " #,
} ,
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " vendor/http_localhost_4545/run/002_hello.ts " ) . unwrap ( ) ,
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true ,
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" range " : {
" start " : {
" line " : 0 ,
" character " : 27 ,
} ,
" end " : {
" line " : 0 ,
" character " : 27 ,
} ,
} ,
" newText " : " \n " ,
} ] ) ,
) ;
client . shutdown ( ) ;
}
2023-09-02 11:36:04 -04:00
// Regression test for https://github.com/denoland/deno/issues/19802.
// Disable the `workspace/configuration` capability. Ensure the LSP falls back
// to using `enablePaths` from the `InitializationOptions`.
#[ test ]
fn lsp_workspace_enable_paths_no_workspace_configuration ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " main_disabled.ts " , " Date.now() " ) ;
temp_dir . write ( " main_enabled.ts " , " Date.now() " ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . with_capabilities ( | capabilities | {
capabilities . workspace . as_mut ( ) . unwrap ( ) . configuration = Some ( false ) ;
} ) ;
builder . set_workspace_folders ( vec! [ lsp ::WorkspaceFolder {
uri : temp_dir . uri ( ) ,
name : " project " . to_string ( ) ,
} ] ) ;
builder . set_root_uri ( temp_dir . uri ( ) ) ;
builder . set_enable_paths ( vec! [ " ./main_enabled.ts " . to_string ( ) ] ) ;
} ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " main_disabled.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " main_disabled.ts " ) ,
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " main_enabled.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " main_enabled.ts " ) ,
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " main_disabled.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 5 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " main_enabled.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 5 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
] ,
" range " : {
" start " : { " line " : 0 , " character " : 5 , } ,
" end " : { " line " : 0 , " character " : 8 , }
}
} )
) ;
client . shutdown ( ) ;
}
2023-10-12 11:07:27 -04:00
#[ test ]
fn lsp_did_change_deno_configuration_notification ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
temp_dir . write ( " deno.json " , json! ( { } ) . to_string ( ) ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 1 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 1 ,
" configurationType " : " denoJson "
} ] ,
} ) )
) ;
temp_dir . write (
" deno.json " ,
json! ( { " fmt " : { " semiColons " : false } } ) . to_string ( ) ,
) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 2 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 2 ,
" configurationType " : " denoJson "
} ] ,
} ) )
) ;
temp_dir . remove_file ( " deno.json " ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 3 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 3 ,
" configurationType " : " denoJson "
} ] ,
} ) )
) ;
temp_dir . write ( " package.json " , json! ( { } ) . to_string ( ) ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 1 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 1 ,
" configurationType " : " packageJson "
} ] ,
} ) )
) ;
temp_dir . write ( " package.json " , json! ( { " type " : " module " } ) . to_string ( ) ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 2 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 2 ,
" configurationType " : " packageJson "
} ] ,
} ) )
) ;
temp_dir . remove_file ( " package.json " ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 3 ,
} ] ,
} ) ) ;
let res = client
. read_notification_with_method ::< Value > ( " deno/didChangeDenoConfiguration " ) ;
assert_eq! (
res ,
Some ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " package.json " ) . unwrap ( ) ,
" type " : 3 ,
" configurationType " : " packageJson "
} ] ,
} ) )
) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_deno_task ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.jsonc " ,
2023-01-12 20:59:13 -05:00
r #" {
" tasks " : {
" build " : " deno test " ,
" some:test " : " deno bundle mod.ts "
}
} " #,
2023-03-08 18:15:20 -05:00
) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.jsonc " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request ( " deno/task " , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [
2023-01-12 20:59:13 -05:00
{
" name " : " build " ,
2023-10-12 11:07:27 -04:00
" detail " : " deno test " ,
" sourceUri " : temp_dir . uri ( ) . join ( " deno.jsonc " ) . unwrap ( ) ,
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" name " : " some:test " ,
2023-10-12 11:07:27 -04:00
" detail " : " deno bundle mod.ts " ,
" sourceUri " : temp_dir . uri ( ) . join ( " deno.jsonc " ) . unwrap ( ) ,
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
] )
2023-01-12 20:59:13 -05:00
) ;
}
2023-10-10 00:53:41 -04:00
#[ test ]
fn lsp_reload_import_registries_command ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let res = client . write_request (
" workspace/executeCommand " ,
json! ( { " command " : " deno.reloadImportRegistries " } ) ,
) ;
assert_eq! ( res , json! ( true ) ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
2023-09-07 09:09:16 -04:00
fn lsp_import_attributes ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " data:application/json;utf8,{ \" imports \" : { \" example \" : \" https://deno.land/x/example/mod.ts \" }} " ) ;
} ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( {
" deno " : {
2023-01-12 20:59:13 -05:00
" enable " : true ,
" codeLens " : {
2023-10-24 16:27:27 -04:00
" test " : true ,
} ,
} ,
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/test.json " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" a \" :1} " ,
} ,
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/a.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import a from \" ./test.json \" ; \n \n console.log(a); \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
json! (
diagnostics
2023-05-26 02:10:18 -04:00
. messages_with_file_and_source ( " file:///a/a.ts " , " deno " )
2023-01-12 20:59:13 -05:00
. diagnostics
) ,
json! ( [
{
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 27 }
2023-01-12 20:59:13 -05:00
} ,
" severity " : 1 ,
2023-09-07 09:09:16 -04:00
" code " : " no-attribute-type " ,
2023-01-12 20:59:13 -05:00
" source " : " deno " ,
2023-09-07 09:09:16 -04:00
" message " : " The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \" json \" }` to the import statement. "
2023-01-12 20:59:13 -05:00
}
] )
) ;
2023-03-09 15:09:03 -05:00
let res = client
2023-01-12 20:59:13 -05:00
. write_request (
" textDocument/codeAction " ,
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/a.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 27 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 27 }
} ,
" severity " : 1 ,
2023-09-07 09:09:16 -04:00
" code " : " no-attribute-type " ,
2023-03-08 18:15:20 -05:00
" source " : " deno " ,
2023-09-07 09:09:16 -04:00
" message " : " The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \" json \" }` to the import statement. "
2023-03-08 18:15:20 -05:00
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
2023-01-12 20:59:13 -05:00
)
2023-03-09 15:09:03 -05:00
;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-09-07 09:09:16 -04:00
" title " : " Insert import attribute. " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 27 }
} ,
" severity " : 1 ,
2023-09-07 09:09:16 -04:00
" code " : " no-attribute-type " ,
2023-03-08 18:15:20 -05:00
" source " : " deno " ,
2023-09-07 09:09:16 -04:00
" message " : " The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \" json \" }` to the import statement. "
2023-03-08 18:15:20 -05:00
}
] ,
" edit " : {
" changes " : {
" file:///a/a.ts " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 27 } ,
" end " : { " line " : 0 , " character " : 27 }
} ,
2023-09-07 09:09:16 -04:00
" newText " : " with { type: \" json \" } "
2023-03-08 18:15:20 -05:00
}
]
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_import_map_import_completions ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
" import-map.json " ,
r #" {
" imports " : {
" /~/ " : " ./lib/ " ,
" fs " : " https://example.com/fs/index.js " ,
" std/ " : " https://example.com/std@0.123.0/ "
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
} " #,
) ;
temp_dir . create_dir_all ( " lib " ) ;
temp_dir . write ( " lib/b.ts " , r # "export const b = "b";"# ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " import-map.json " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let uri = temp_dir . uri ( ) . join ( " a.ts " ) . unwrap ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" /~/b.ts \" ; \n import * as b from \" \" "
}
} ) ) ;
let res = client . get_completion (
& uri ,
( 1 , 20 ) ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" triggerKind " : 2 ,
" triggerCharacter " : " \" "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
json! ( res ) ,
json! ( {
2023-01-12 20:59:13 -05:00
" isIncomplete " : false ,
" items " : [
{
" label " : " . " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " . " ,
" commitCharacters " : [ " \" " , " ' " ] ,
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " .. " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " .. " ,
" commitCharacters " : [ " \" " , " ' " ] ,
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " std " ,
" kind " : 19 ,
" detail " : " (import map) " ,
" sortText " : " std " ,
" insertText " : " std " ,
" commitCharacters " : [ " \" " , " ' " ] ,
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " fs " ,
" kind " : 17 ,
" detail " : " (import map) " ,
" sortText " : " fs " ,
" insertText " : " fs " ,
" commitCharacters " : [ " \" " , " ' " ] ,
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " /~ " ,
" kind " : 19 ,
" detail " : " (import map) " ,
" sortText " : " /~ " ,
" insertText " : " /~ " ,
" commitCharacters " : [ " \" " , " ' " ] ,
}
]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : uri ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 1 , " character " : 20 } ,
" end " : { " line " : 1 , " character " : 20 }
} ,
" text " : " /~/ "
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
let res = client . get_completion (
uri ,
( 1 , 23 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " / "
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
json! ( res ) ,
json! ( {
2023-01-12 20:59:13 -05:00
" isIncomplete " : false ,
" items " : [
{
" label " : " b.ts " ,
" kind " : 9 ,
" detail " : " (import map) " ,
" sortText " : " 1 " ,
" filterText " : " /~/b.ts " ,
" textEdit " : {
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 1 , " character " : 20 } ,
" end " : { " line " : 1 , " character " : 23 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " /~/b.ts "
} ,
" commitCharacters " : [ " \" " , " ' " ] ,
}
]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 19 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " const Deno.args: string[] "
} ,
2023-03-05 12:47:04 -05:00
" 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 ```ts \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. " ,
2023-01-12 20:59:13 -05:00
" \n \n *@category* - Runtime Environment " ,
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 17 } ,
" end " : { " line " : 0 , " character " : 21 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_asset ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
client . write_request (
" textDocument/definition " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 14 }
} ) ,
) ;
client . write_request (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.es2015.symbol.wellknown.d.ts "
} ,
2023-03-21 11:46:40 -04:00
" position " : { " line " : 111 , " character " : 13 }
2023-03-09 15:09:03 -05:00
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " interface Date " ,
} ,
" Enables basic storage and retrieval of dates and times. "
] ,
" range " : {
2023-03-21 11:46:40 -04:00
" start " : { " line " : 111 , " character " : 10 , } ,
" end " : { " line " : 111 , " character " : 14 , }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_disabled ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_deno_enable ( false ) ;
} ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( { " deno " : { " enable " : false } } ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n " ,
} ,
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_inlay_hints ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . enable_inlay_hints ( ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" function a(b: string) {
2023-01-12 20:59:13 -05:00
return b ;
}
a ( " foo " ) ;
enum C {
A ,
}
parseInt ( " 123 " , 8 ) ;
const d = Date . now ( ) ;
class E {
f = Date . now ( ) ;
}
[ " a " ] . map ( ( v ) = > v + v ) ;
" #
2023-03-09 15:09:03 -05:00
}
} ) ) ;
let res = client . write_request (
" textDocument/inlayHint " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 19 , " character " : 0 , }
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
2023-01-12 20:59:13 -05:00
json! ( [
{
2023-03-08 18:15:20 -05:00
" position " : { " line " : 0 , " character " : 21 } ,
2023-01-12 20:59:13 -05:00
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 4 , " character " : 10 } ,
2023-01-12 20:59:13 -05:00
" label " : " b: " ,
" kind " : 2 ,
" paddingRight " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 7 , " character " : 11 } ,
2023-01-12 20:59:13 -05:00
" label " : " = 0 " ,
" paddingLeft " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 10 , " character " : 17 } ,
2023-01-12 20:59:13 -05:00
" label " : " string: " ,
" kind " : 2 ,
" paddingRight " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 10 , " character " : 24 } ,
2023-01-12 20:59:13 -05:00
" label " : " radix: " ,
" kind " : 2 ,
" paddingRight " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 12 , " character " : 15 } ,
2023-01-12 20:59:13 -05:00
" label " : " : number " ,
" kind " : 1 ,
" paddingLeft " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 15 , " character " : 11 } ,
2023-01-12 20:59:13 -05:00
" label " : " : number " ,
" kind " : 1 ,
" paddingLeft " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 18 , " character " : 18 } ,
2023-01-12 20:59:13 -05:00
" label " : " callbackfn: " ,
" kind " : 2 ,
" paddingRight " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 18 , " character " : 20 } ,
2023-01-12 20:59:13 -05:00
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
2023-03-08 18:15:20 -05:00
} , {
" position " : { " line " : 18 , " character " : 21 } ,
2023-01-12 20:59:13 -05:00
" label " : " : string " ,
" kind " : 1 ,
" paddingLeft " : true
}
] )
) ;
}
#[ test ]
fn lsp_inlay_hints_not_enabled ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" function a(b: string) {
2023-01-12 20:59:13 -05:00
return b ;
}
a ( " foo " ) ;
enum C {
A ,
}
parseInt ( " 123 " , 8 ) ;
const d = Date . now ( ) ;
class E {
f = Date . now ( ) ;
}
[ " a " ] . map ( ( v ) = > v + v ) ;
" #
2023-03-09 15:09:03 -05:00
}
} ) ) ;
let res = client . write_request (
" textDocument/inlayHint " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 19 , " character " : 0 , }
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
2023-09-13 12:30:27 -04:00
fn lsp_workspace_disable_enable_paths ( ) {
2023-03-22 21:58:38 -04:00
fn run_test ( use_trailing_slash : bool ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-22 21:58:38 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-30 17:47:53 -04:00
temp_dir . create_dir_all ( " worker " ) ;
temp_dir . write ( " worker/shared.ts " , " export const a = 1 " ) ;
temp_dir . write ( " worker/other.ts " , " import { a } from './shared.ts'; \n a; " ) ;
2023-09-13 12:30:27 -04:00
temp_dir . write ( " worker/node.ts " , " Buffer.alloc(1); " ) ;
2023-03-22 21:58:38 -04:00
let root_specifier = temp_dir . uri ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_with_config (
| builder | {
builder
2023-09-13 12:30:27 -04:00
. set_disable_paths ( vec! [ " ./worker/node.ts " . to_string ( ) ] )
2023-03-22 21:58:38 -04:00
. set_enable_paths ( vec! [ " ./worker " . to_string ( ) ] )
. set_root_uri ( root_specifier . clone ( ) )
. set_workspace_folders ( vec! [ lsp ::WorkspaceFolder {
uri : if use_trailing_slash {
root_specifier . clone ( )
} else {
ModuleSpecifier ::parse (
root_specifier . as_str ( ) . strip_suffix ( '/' ) . unwrap ( ) ,
)
. unwrap ( )
} ,
name : " project " . to_string ( ) ,
} ] )
. set_deno_enable ( false ) ;
} ,
2023-09-21 01:46:39 -04:00
json! ( { " deno " : {
2023-03-22 21:58:38 -04:00
" enable " : false ,
2023-09-13 12:30:27 -04:00
" disablePaths " : [ " ./worker/node.ts " ] ,
2023-03-22 21:58:38 -04:00
" enablePaths " : [ " ./worker " ] ,
2023-09-21 01:46:39 -04:00
} } ) ,
2023-03-22 21:58:38 -04:00
) ;
2023-01-12 20:59:13 -05:00
2023-03-22 21:58:38 -04:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : root_specifier . join ( " ./file.ts " ) . unwrap ( ) ,
2023-03-22 21:58:38 -04:00
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-22 21:58:38 -04:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
2023-03-22 21:58:38 -04:00
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-22 21:58:38 -04:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
2023-03-22 21:58:38 -04:00
" languageId " : " typescript " ,
" version " : 1 ,
2023-03-30 17:47:53 -04:00
" text " : concat ! (
" console.log(Date.now()); \n " ,
" import { a } from './shared.ts'; \n " ,
" a; \n " ,
) ,
2023-01-12 20:59:13 -05:00
}
2023-03-22 21:58:38 -04:00
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-22 21:58:38 -04:00
client . did_open ( json! ( {
2023-03-09 15:09:03 -05:00
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/subdir/file.ts " ) . unwrap ( ) ,
2023-03-22 21:58:38 -04:00
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
2023-01-12 20:59:13 -05:00
}
2023-03-22 21:58:38 -04:00
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-22 21:58:38 -04:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-09-13 12:30:27 -04:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/node.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 0 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-03-22 21:58:38 -04:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
] ,
" range " : {
" start " : { " line " : 0 , " character " : 17 , } ,
" end " : { " line " : 0 , " character " : 20 , }
}
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/subdir/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
] ,
" range " : {
" start " : { " line " : 0 , " character " : 17 , } ,
" end " : { " line " : 0 , " character " : 20 , }
}
} )
) ;
2023-03-30 17:47:53 -04:00
// check that the file system documents were auto-discovered
// via the enabled paths
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 2 , " character " : 0 } ,
" context " : {
" includeDeclaration " : true
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 1 , " character " : 9 } ,
" end " : { " line " : 1 , " character " : 10 }
}
} , {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 2 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 1 }
}
} , {
" uri " : root_specifier . join ( " ./worker/shared.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 0 , " character " : 13 } ,
" end " : { " line " : 0 , " character " : 14 }
}
} , {
" uri " : root_specifier . join ( " ./worker/other.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
}
} , {
" uri " : root_specifier . join ( " ./worker/other.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 1 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 1 }
}
} ] )
) ;
2023-03-22 21:58:38 -04:00
client . shutdown ( ) ;
}
run_test ( true ) ;
run_test ( false ) ;
2023-01-12 20:59:13 -05:00
}
2023-07-10 21:03:19 -04:00
#[ test ]
fn lsp_exclude_config ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . create_dir_all ( " other " ) ;
temp_dir . write (
" other/shared.ts " ,
// this should not be found in the "find references" since this file is excluded
" import { a } from '../worker/shared.ts'; console.log(a); " ,
) ;
temp_dir . create_dir_all ( " worker " ) ;
temp_dir . write ( " worker/shared.ts " , " export const a = 1 " ) ;
temp_dir . write (
" deno.json " ,
r #" {
" exclude " : [ " other " ] ,
} " #,
) ;
let root_specifier = temp_dir . uri ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : concat ! (
" console.log(Date.now()); \n " ,
" import { a } from './shared.ts'; \n " ,
" a; \n " ,
) ,
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/subdir/file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./other/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 19 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : [
{
" language " : " typescript " ,
" value " : " (method) DateConstructor.now(): number " ,
} ,
" Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC). "
] ,
" range " : {
" start " : { " line " : 0 , " character " : 17 , } ,
" end " : { " line " : 0 , " character " : 20 , }
}
} )
) ;
// check that the file system documents were auto-discovered
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 2 , " character " : 0 } ,
" context " : {
" includeDeclaration " : true
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 1 , " character " : 9 } ,
" end " : { " line " : 1 , " character " : 10 }
}
} , {
" uri " : root_specifier . join ( " ./worker/file.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 2 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 1 }
}
} , {
" uri " : root_specifier . join ( " ./worker/shared.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 0 , " character " : 13 } ,
" end " : { " line " : 0 , " character " : 14 }
}
} ] )
) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_hover_unstable_disabled ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
2023-11-12 17:30:04 -05:00
// IMPORTANT: If you change this API due to stabilization, also change it
// in the enabled test below.
" text " : " type _ = Deno.ForeignLibraryInterface; \n "
2023-03-09 15:09:03 -05:00
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
2023-11-12 17:30:04 -05:00
" position " : { " line " : 0 , " character " : 14 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
2023-11-12 17:30:04 -05:00
" value " : " type Deno.ForeignLibraryInterface = /*unresolved*/ any " ,
} ,
" " ,
2023-01-12 20:59:13 -05:00
] ,
" range " : {
2023-11-12 17:30:04 -05:00
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 37 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_unstable_enabled ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder . set_unstable ( true ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
2023-11-12 17:30:04 -05:00
" text " : " type _ = Deno.ForeignLibraryInterface; \n "
2023-03-09 15:09:03 -05:00
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
2023-11-12 17:30:04 -05:00
" position " : { " line " : 0 , " character " : 14 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " :[
{
" language " :" typescript " ,
2023-11-12 17:30:04 -05:00
" value " :" interface Deno.ForeignLibraryInterface "
2023-01-12 20:59:13 -05:00
} ,
2023-11-12 17:30:04 -05:00
" **UNSTABLE**: New API, yet to be vetted. \n \n A foreign library interface descriptor. " ,
" \n \n *@category* - FFI " ,
2023-01-12 20:59:13 -05:00
] ,
" range " :{
2023-11-12 17:30:04 -05:00
" start " :{ " line " :0 , " character " :14 } ,
" end " :{ " line " :0 , " character " :37 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_change_mbc ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const a = `编写软件很难`; \n const b = `👍🦕😃`; \n console.log(a, b); \n "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
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
2023-06-26 09:10:27 -04:00
// after the deno emoji is character index 15
2023-03-09 15:09:03 -05:00
" character " : 15
}
} ,
" text " : " "
}
]
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 2 , " character " : 15 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " const b: \" 😃 \" " ,
} ,
" " ,
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 2 , " character " : 15 , } ,
" end " : { " line " : 2 , " character " : 16 , } ,
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_closed_document ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write ( " a.ts " , r # "export const a = "a";"# ) ;
temp_dir . write ( " b.ts " , r # "export * from "./a.ts";"# ) ;
temp_dir . write ( " c.ts " , " import { a } from \" ./b.ts \" ; \n console.log(a); \n " ) ;
let b_specifier = temp_dir . uri ( ) . join ( " b.ts " ) . unwrap ( ) ;
let c_specifier = temp_dir . uri ( ) . join ( " c.ts " ) . unwrap ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : b_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export * from " . / a . ts " ; " #
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : c_specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { a } from \" ./b.ts \" ; \n console.log(a); \n " ,
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : { " line " : 0 , " character " : 10 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " (alias) const a: \" a \" \n import a "
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
client . write_notification (
" textDocument/didClose " ,
json! ( {
" textDocument " : {
" uri " : b_specifier ,
}
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : c_specifier ,
} ,
" position " : { " line " : 0 , " character " : 10 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " (alias) const a: \" a \" \n import a "
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_dependency ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ) ;
client . did_open (
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts " ,
2023-01-12 20:59:13 -05:00
" languageId " : " typescript " ,
" version " : 1 ,
2023-03-09 15:09:03 -05:00
" text " : " import * as a from \" http://127.0.0.1:4545/xTypeScriptTypes.js \" ; \n // @deno-types= \" http://127.0.0.1:4545/type_definitions/foo.d.ts \" \n import * as b from \" http://127.0.0.1:4545/type_definitions/foo.js \" ; \n import * as c from \" http://127.0.0.1:4545/subdir/type_reference.js \" ; \n import * as d from \" http://127.0.0.1:4545/subdir/mod1.ts \" ; \n import * as e from \" data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo= \" ; \n import * as f from \" ./file_01.ts \" ; \n import * as g from \" http://localhost:4545/x/a/mod.ts \" ; \n \n console.log(a, b, c, d, e, f, g); \n "
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
2023-03-09 15:09:03 -05:00
} ,
" position " : { " line " : 0 , " character " : 28 }
2023-03-08 18:15:20 -05:00
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 19 } ,
" end " :{ " line " : 0 , " character " : 62 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 3 , " character " : 28 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 3 , " character " : 19 } ,
" end " :{ " line " : 3 , " character " : 67 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 4 , " character " : 28 }
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://127.0.0.1:4545/subdir/mod1.ts \n "
2023-01-12 20:59:13 -05:00
} ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 4 , " character " : 19 } ,
" end " :{ " line " : 4 , " character " : 57 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 5 , " character " : 28 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: _(a data url)_ \n "
} ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 5 , " character " : 19 } ,
" end " :{ " line " : 5 , " character " : 132 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 6 , " character " : 28 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: file​:///a/file_01.ts \n "
} ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 6 , " character " : 19 } ,
" end " :{ " line " : 6 , " character " : 33 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
}
// 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 ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file1.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export type Foo = { bar(): string }; \n "
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { Foo } from './file1.ts'; declare const f: Foo; f \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file2.ts "
} ,
" position " : { " line " : 0 , " character " : 56 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " const f: Foo " ,
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 56 , } ,
" end " : { " line " : 0 , " character " : 57 , }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
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 " : " . "
}
]
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file2.ts "
} ,
" position " : { " line " : 0 , " character " : 56 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " const f: Foo " ,
} ,
" "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 56 , } ,
" end " : { " line " : 0 , " character " : 57 , }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_typescript_types ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 " ,
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " http://127.0.0.1:4545/xTypeScriptTypes.js " ] ,
" file:///a/file.ts " ,
2023-03-09 15:09:03 -05:00
] ,
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 24 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
2023-01-12 20:59:13 -05:00
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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 62 }
2023-01-12 20:59:13 -05:00
}
} )
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_hover_jsdoc_symbol_link ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export function hello() {} \n "
}
} ) ) ;
client . did_open (
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts " ,
2023-01-12 20:59:13 -05:00
" languageId " : " typescript " ,
" version " : 1 ,
2023-03-09 15:09:03 -05:00
" 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 "
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 7 , " character " : 10 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : [
{
" language " : " typescript " ,
" value " : " function a(): void "
} ,
" JSDoc [hello](file:///a/file.ts#L1,10) and [`b`](file:///a/file.ts#L5,7) "
] ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 7 , " character " : 9 } ,
" end " : { " line " : 7 , " character " : 10 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_goto_type_definition ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 " ,
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/typeDefinition " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 12 , " character " : 1 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [
2023-01-12 20:59:13 -05:00
{
" targetUri " : " file:///a/file.ts " ,
" targetRange " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 4 , " character " : 0 } ,
" end " : { " line " : 9 , " character " : 1 }
2023-01-12 20:59:13 -05:00
} ,
" targetSelectionRange " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 4 , " character " : 13 } ,
" end " : { " line " : 4 , " character " : 14 }
2023-01-12 20:59:13 -05:00
}
}
2023-03-09 15:09:03 -05:00
] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_call_hierarchy ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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(); "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/prepareCallHierarchy " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 5 , " character " : 3 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" name " : " baz " ,
" kind " : 6 ,
" detail " : " Bar " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 7 , " character " : 3 }
} ,
" selectionRange " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 5 , " character " : 5 }
}
2023-03-09 15:09:03 -05:00
} ] )
2023-03-08 18:15:20 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" callHierarchy/incomingCalls " ,
json! ( {
" item " : {
" name " : " baz " ,
" kind " : 6 ,
" detail " : " Bar " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 7 , " character " : 3 }
} ,
" selectionRange " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 5 , " character " : 5 }
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" from " : {
" name " : " main " ,
" kind " : 12 ,
" detail " : " " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 10 , " character " : 0 } ,
" end " : { " line " : 13 , " character " : 1 }
} ,
" selectionRange " : {
" start " : { " line " : 10 , " character " : 9 } ,
" end " : { " line " : 10 , " character " : 13 }
}
} ,
" fromRanges " : [
{
" start " : { " line " : 12 , " character " : 6 } ,
" end " : { " line " : 12 , " character " : 9 }
}
]
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" callHierarchy/outgoingCalls " ,
json! ( {
" item " : {
" name " : " baz " ,
" kind " : 6 ,
" detail " : " Bar " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 7 , " character " : 3 }
} ,
" selectionRange " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 5 , " character " : 5 }
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" to " : {
" name " : " foo " ,
" kind " : 12 ,
" detail " : " " ,
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 1 }
} ,
" selectionRange " : {
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 12 }
}
} ,
" fromRanges " : [ {
" start " : { " line " : 6 , " character " : 11 } ,
" end " : { " line " : 6 , " character " : 14 }
} ]
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_large_doc_changes ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
let large_file_text =
fs ::read_to_string ( testdata_path ( ) . join ( " lsp " ) . join ( " large_file.txt " ) )
. unwrap ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " javascript " ,
" version " : 1 ,
" text " : large_file_text ,
}
} ) ) ;
client . write_notification (
" textDocument/didChange " ,
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
2023-03-09 15:09:03 -05:00
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 444 , " character " : 11 } ,
" end " : { " line " : 444 , " character " : 14 }
} ,
" text " : " +++ "
}
]
} ) ,
) ;
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 " : " // "
}
]
} ) ,
) ;
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 "
}
]
} ) ,
) ;
client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 421 , " character " : 30 }
} ) ,
) ;
client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 444 , " character " : 6 }
} ) ,
) ;
client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 461 , " character " : 34 }
} ) ,
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
assert! ( client . duration ( ) . as_millis ( ) < = 15000 ) ;
}
#[ test ]
fn lsp_document_symbol ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
2023-06-26 09:10:27 -04:00
" text " : " interface IFoo { \n foo(): boolean; \n } \n \n class Bar implements IFoo { \n constructor(public x: number) { } \n foo() { return true; } \n /** @deprecated */ \n baz() { return false; } \n get value(): number { return 0; } \n set value(_newValue: number) { return; } \n static staticBar = new Bar(0); \n private static getStaticBar() { return Bar.staticBar; } \n } \n \n enum Values { value1, value2 } \n \n var bar: IFoo = new Bar(3); "
2023-03-08 18:15:20 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/documentSymbol " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" name " : " bar " ,
" kind " : 13 ,
" range " : {
" start " : { " line " : 17 , " character " : 4 } ,
" end " : { " line " : 17 , " character " : 26 }
} ,
" selectionRange " : {
" start " : { " line " : 17 , " character " : 4 } ,
" end " : { " line " : 17 , " character " : 7 }
}
} , {
" name " : " Bar " ,
" kind " : 5 ,
" range " : {
" start " : { " line " : 4 , " character " : 0 } ,
" end " : { " line " : 13 , " character " : 1 }
} ,
" selectionRange " : {
" start " : { " line " : 4 , " character " : 6 } ,
" end " : { " line " : 4 , " character " : 9 }
} ,
" children " : [ {
" name " : " constructor " ,
" kind " : 9 ,
" range " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 5 , " character " : 35 }
} ,
" selectionRange " : {
" start " : { " line " : 5 , " character " : 2 } ,
" end " : { " line " : 5 , " character " : 35 }
}
} , {
" name " : " baz " ,
" kind " : 6 ,
" tags " : [ 1 ] ,
" range " : {
" start " : { " line " : 8 , " character " : 2 } ,
" end " : { " line " : 8 , " character " : 25 }
} ,
" selectionRange " : {
" start " : { " line " : 8 , " character " : 2 } ,
" end " : { " line " : 8 , " character " : 5 }
}
} , {
" name " : " foo " ,
" kind " : 6 ,
" range " : {
" start " : { " line " : 6 , " character " : 2 } ,
" end " : { " line " : 6 , " character " : 24 }
} ,
" selectionRange " : {
" start " : { " line " : 6 , " character " : 2 } ,
" end " : { " line " : 6 , " character " : 5 }
}
} , {
" name " : " getStaticBar " ,
" kind " : 6 ,
" range " : {
" start " : { " line " : 12 , " character " : 2 } ,
" end " : { " line " : 12 , " character " : 57 }
} ,
" selectionRange " : {
" start " : { " line " : 12 , " character " : 17 } ,
" end " : { " line " : 12 , " character " : 29 }
}
} , {
" name " : " staticBar " ,
" kind " : 8 ,
" range " : {
" start " : { " line " : 11 , " character " : 2 } ,
" end " : { " line " : 11 , " character " : 32 }
} ,
" selectionRange " : {
" start " : { " line " : 11 , " character " : 9 } ,
" end " : { " line " : 11 , " character " : 18 }
}
} , {
" name " : " (get) value " ,
" kind " : 8 ,
" range " : {
" start " : { " line " : 9 , " character " : 2 } ,
" end " : { " line " : 9 , " character " : 35 }
} ,
" selectionRange " : {
" start " : { " line " : 9 , " character " : 6 } ,
" end " : { " line " : 9 , " character " : 11 }
}
} , {
" name " : " (set) value " ,
" kind " : 8 ,
" range " : {
" start " : { " line " : 10 , " character " : 2 } ,
" end " : { " line " : 10 , " character " : 42 }
} ,
" selectionRange " : {
" start " : { " line " : 10 , " character " : 6 } ,
" end " : { " line " : 10 , " character " : 11 }
}
} , {
" name " : " x " ,
" kind " : 8 ,
" range " : {
" start " : { " line " : 5 , " character " : 14 } ,
" end " : { " line " : 5 , " character " : 30 }
} ,
" selectionRange " : {
" start " : { " line " : 5 , " character " : 21 } ,
" end " : { " line " : 5 , " character " : 22 }
}
} ]
} , {
" name " : " IFoo " ,
" kind " : 11 ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 1 }
} ,
" selectionRange " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 14 }
} ,
" children " : [ {
" name " : " foo " ,
" kind " : 6 ,
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 17 }
} ,
" selectionRange " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 5 }
}
} ]
} , {
" name " : " Values " ,
" kind " : 10 ,
" range " : {
" start " : { " line " : 15 , " character " : 0 } ,
" end " : { " line " : 15 , " character " : 30 }
} ,
" selectionRange " : {
" start " : { " line " : 15 , " character " : 5 } ,
" end " : { " line " : 15 , " character " : 11 }
} ,
" children " : [ {
" name " : " value1 " ,
" kind " : 22 ,
" range " : {
" start " : { " line " : 15 , " character " : 14 } ,
" end " : { " line " : 15 , " character " : 20 }
} ,
" selectionRange " : {
" start " : { " line " : 15 , " character " : 14 } ,
" end " : { " line " : 15 , " character " : 20 }
}
} , {
" name " : " value2 " ,
" kind " : 22 ,
" range " : {
" start " : { " line " : 15 , " character " : 22 } ,
" end " : { " line " : 15 , " character " : 28 }
} ,
" selectionRange " : {
" start " : { " line " : 15 , " character " : 22 } ,
" end " : { " line " : 15 , " character " : 28 }
}
} ]
} ]
2023-03-09 15:09:03 -05:00
)
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_folding_range ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/foldingRange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" startLine " : 0 ,
" endLine " : 12 ,
" kind " : " region "
} , {
" startLine " : 1 ,
" endLine " : 3 ,
" kind " : " comment "
} , {
" startLine " : 4 ,
" endLine " : 10
} , {
" startLine " : 5 ,
" endLine " : 9
} , {
" startLine " : 6 ,
" endLine " : 7
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_rename ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/rename " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 4 } ,
" newName " : " variable_modified "
} ) ,
) ;
2023-03-08 18:15:20 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 4 } ,
" end " : { " line " : 0 , " character " : 12 }
} ,
" newText " : " variable_modified "
} , {
" range " : {
" start " : { " line " : 1 , " character " : 12 } ,
" end " : { " line " : 1 , " character " : 20 }
} ,
" newText " : " variable_modified "
} ]
} ]
2023-03-09 15:09:03 -05:00
} )
2023-03-08 18:15:20 -05:00
) ;
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_selection_range ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 } "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/selectionRange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" positions " : [ { " line " : 2 , " character " : 8 } ]
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 2 , " character " : 8 } ,
" end " : { " line " : 2 , " character " : 9 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 2 , " character " : 8 } ,
" end " : { " line " : 2 , " character " : 15 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 2 , " character " : 4 } ,
" end " : { " line " : 4 , " character " : 5 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 1 , " character " : 13 } ,
" end " : { " line " : 6 , " character " : 2 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 1 , " character " : 12 } ,
" end " : { " line " : 6 , " character " : 3 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 6 , " character " : 3 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 0 , " character " : 11 } ,
" end " : { " line " : 7 , " character " : 0 }
} ,
" parent " : {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 7 , " character " : 1 }
}
}
}
}
}
}
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_semantic_tokens ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " enum Values { value1, value2 } \n \n async function baz(s: string): Promise<string> { \n const r = s.slice(0); \n return r; \n } \n \n interface IFoo { \n readonly x: number; \n foo(): boolean; \n } \n \n class Bar implements IFoo { \n constructor(public readonly x: number) { } \n foo() { return true; } \n static staticBar = new Bar(0); \n private static getStaticBar() { return Bar.staticBar; } \n } \n "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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
]
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/semanticTokens/range " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 6 , " character " : 0 }
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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
]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_lens ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-30 12:15:21 -04:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : concat ! (
" 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 " ,
" const b = 2; \n " ,
" const c = 3; \n " ,
" c; c; " ,
) ,
}
} ) ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-03-08 18:15:20 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 6 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 3 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
2023-03-09 15:09:03 -05:00
} ] )
) ;
let res = 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 "
}
} ) ,
2023-03-08 18:15:20 -05:00
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 6 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" command " : {
2023-03-30 12:15:21 -04:00
" title " : " 1 reference " ,
2023-03-08 18:15:20 -05:00
" command " : " deno.showReferences " ,
" arguments " : [
" file:///a/file.ts " ,
{ " line " : 0 , " character " : 6 } ,
[ {
" uri " : " file:///a/file.ts " ,
" range " : {
2023-03-30 12:15:21 -04:00
" start " : { " line " : 12 , " character " : 14 } ,
" end " : { " line " : 12 , " character " : 15 }
}
} ]
]
}
} )
) ;
// 0 references
let res = client . write_request (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : { " line " : 14 , " character " : 6 } ,
" end " : { " line " : 14 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" range " : {
" start " : { " line " : 14 , " character " : 6 } ,
" end " : { " line " : 14 , " character " : 7 }
} ,
" command " : {
" title " : " 0 references " ,
" command " : " " ,
}
} )
) ;
// 2 references
let res = client . write_request (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : { " line " : 15 , " character " : 6 } ,
" end " : { " line " : 15 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" range " : {
" start " : { " line " : 15 , " character " : 6 } ,
" end " : { " line " : 15 , " character " : 7 }
} ,
" command " : {
" title " : " 2 references " ,
" command " : " deno.showReferences " ,
" arguments " : [
" file:///a/file.ts " ,
{ " line " : 15 , " character " : 6 } ,
[ {
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 16 , " character " : 0 } ,
" end " : { " line " : 16 , " character " : 1 }
2023-03-08 18:15:20 -05:00
}
2023-03-30 12:15:21 -04:00
} , {
2023-03-08 18:15:20 -05:00
" uri " : " file:///a/file.ts " ,
" range " : {
2023-03-30 12:15:21 -04:00
" start " : { " line " : 16 , " character " : 3 } ,
" end " : { " line " : 16 , " character " : 4 }
2023-03-08 18:15:20 -05:00
}
} ]
]
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-30 12:15:21 -04:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_lens_impl ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} , {
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 4 , " character " : 6 } ,
" end " : { " line " : 4 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} , {
" range " : {
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 11 , " character " : 2 } ,
" end " : { " line " : 11 , " character " : 3 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
2023-03-09 15:09:03 -05:00
} ] )
) ;
let res = 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 "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" command " : {
" title " : " 1 implementation " ,
" command " : " deno.showReferences " ,
" arguments " : [
" file:///a/file.ts " ,
{ " line " : 0 , " character " : 10 } ,
[ {
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 4 , " character " : 6 } ,
" end " : { " line " : 4 , " character " : 7 }
}
} ]
]
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" codeLens/resolve " ,
json! ( {
" range " : {
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
2023-01-12 20:59:13 -05:00
} ,
" command " : {
" title " : " 0 implementations " ,
" command " : " "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_lens_test ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder . disable_testing_api ( ) . set_code_lens ( None ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const { test } = Deno; \n const { test: test2 } = Deno; \n const test3 = Deno.test; \n \n Deno.test( \" test a \" , () => {}); \n Deno.test({ \n name: \" test b \" , \n fn() {}, \n }); \n test({ \n name: \" test c \" , \n fn() {}, \n }); \n test( \" test d \" , () => {}); \n test2({ \n name: \" test e \" , \n fn() {}, \n }); \n test2( \" test f \" , () => {}); \n test3({ \n name: \" test g \" , \n fn() {}, \n }); \n test3( \" test h \" , () => {}); \n "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 4 , " character " : 5 } ,
" end " : { " line " : 4 , " character " : 9 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test a " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 4 , " character " : 5 } ,
" end " : { " line " : 4 , " character " : 9 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test a " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 5 , " character " : 5 } ,
" end " : { " line " : 5 , " character " : 9 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test b " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 5 , " character " : 5 } ,
" end " : { " line " : 5 , " character " : 9 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test b " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 9 , " character " : 0 } ,
" end " : { " line " : 9 , " character " : 4 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test c " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 9 , " character " : 0 } ,
" end " : { " line " : 9 , " character " : 4 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test c " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 13 , " character " : 0 } ,
" end " : { " line " : 13 , " character " : 4 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test d " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 13 , " character " : 0 } ,
" end " : { " line " : 13 , " character " : 4 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test d " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 14 , " character " : 0 } ,
" end " : { " line " : 14 , " character " : 5 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test e " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 14 , " character " : 0 } ,
" end " : { " line " : 14 , " character " : 5 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test e " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 18 , " character " : 0 } ,
" end " : { " line " : 18 , " character " : 5 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test f " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 18 , " character " : 0 } ,
" end " : { " line " : 18 , " character " : 5 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test f " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 19 , " character " : 0 } ,
" end " : { " line " : 19 , " character " : 5 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test g " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 19 , " character " : 0 } ,
" end " : { " line " : 19 , " character " : 5 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test g " ,
{ " inspect " : true }
]
}
} , {
" range " : {
" start " : { " line " : 23 , " character " : 0 } ,
" end " : { " line " : 23 , " character " : 5 }
} ,
" command " : {
" title " : " ▶︎ Run Test " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test h " ,
{ " inspect " : false }
]
}
} , {
" range " : {
" start " : { " line " : 23 , " character " : 0 } ,
" end " : { " line " : 23 , " character " : 5 }
} ,
" command " : {
" title " : " Debug " ,
" command " : " deno.test " ,
" arguments " : [
" file:///a/file.ts " ,
" test h " ,
{ " inspect " : true }
]
}
2023-03-09 15:09:03 -05:00
} ] )
2023-03-08 18:15:20 -05:00
) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_code_lens_test_disabled ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder . disable_testing_api ( ) . set_code_lens ( Some ( json! ( {
" implementations " : true ,
" references " : true ,
" test " : false
} ) ) ) ;
} ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( {
" deno " : {
" enable " : true ,
" codeLens " : {
" test " : false ,
} ,
} ,
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const { test } = Deno; \n const { test: test2 } = Deno; \n const test3 = Deno.test; \n \n Deno.test( \" test a \" , () => {}); \n Deno.test({ \n name: \" test b \" , \n fn() {}, \n }); \n test({ \n name: \" test c \" , \n fn() {}, \n }); \n test( \" test d \" , () => {}); \n test2({ \n name: \" test e \" , \n fn() {}, \n }); \n test2( \" test f \" , () => {}); \n test3({ \n name: \" test g \" , \n fn() {}, \n }); \n test3( \" test h \" , () => {}); \n "
} ,
} ) ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-10-24 16:27:27 -04:00
assert_eq! ( res , json! ( null ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_lens_non_doc_nav_tree ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Date.now()); \n "
}
} ) ) ;
client . write_request (
" textDocument/references " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 3 } ,
" context " : {
" includeDeclaration " : true
}
} ) ,
) ;
client . write_request (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
}
} ) ,
) ;
let res = client . write_request_with_res_as ::< Vec < lsp ::CodeLens > > (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " deno:/asset/lib.deno.shared_globals.d.ts "
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert! ( res . len ( ) > 50 ) ;
2023-03-09 15:09:03 -05:00
client . write_request_with_res_as ::< 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 "
}
} ) ,
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_nav_tree_updates ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} , {
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 4 , " character " : 6 } ,
" end " : { " line " : 4 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} , {
" range " : {
" start " : { " line " : 10 , " character " : 10 } ,
" end " : { " line " : 10 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 11 , " character " : 2 } ,
" end " : { " line " : 11 , " character " : 3 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
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 " : " "
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
let res = client . write_request (
" textDocument/codeLens " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " implementations "
}
} , {
" range " : {
" start " : { " line " : 0 , " character " : 10 } ,
" end " : { " line " : 0 , " character " : 11 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
} , {
" range " : {
" start " : { " line " : 4 , " character " : 6 } ,
" end " : { " line " : 4 , " character " : 7 }
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" source " : " references "
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-03-30 12:15:21 -04:00
#[ test ]
fn lsp_find_references ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-30 12:15:21 -04:00
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/mod.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
2023-08-27 00:04:12 -04:00
" text " : r " export const a = 1; \n const b = 2; "
2023-03-30 12:15:21 -04:00
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/mod.test.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" import { a } from './mod.ts'; console.log(a); " #
}
} ) ) ;
// test without including the declaration
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/mod.ts " ,
} ,
" position " : { " line " : 0 , " character " : 13 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : " file:///a/mod.test.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
}
} , {
" uri " : " file:///a/mod.test.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 42 } ,
" end " : { " line " : 0 , " character " : 43 }
}
} ] )
) ;
// test with including the declaration
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/mod.ts " ,
} ,
" position " : { " line " : 0 , " character " : 13 } ,
" context " : {
" includeDeclaration " : true
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : " file:///a/mod.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 13 } ,
" end " : { " line " : 0 , " character " : 14 }
}
} , {
" uri " : " file:///a/mod.test.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
}
} , {
" uri " : " file:///a/mod.test.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 42 } ,
" end " : { " line " : 0 , " character " : 43 }
}
} ] )
) ;
// test 0 references
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/mod.ts " ,
} ,
" position " : { " line " : 1 , " character " : 6 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ; // seems it always returns null for this, which is ok
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_signature_help ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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( "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/signatureHelp " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " character " : 4 , " line " : 9 } ,
" context " : {
" triggerKind " : 2 ,
" triggerCharacter " : " ( " ,
" isRetrigger " : false
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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. "
}
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " b: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a second number. "
}
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 0
2023-03-09 15:09:03 -05:00
} )
) ;
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, "
}
]
} ) ,
) ;
let res = client . write_request (
" textDocument/signatureHelp " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" position " : { " character " : 8 , " line " : 9 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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. "
}
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" label " : " b: number " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " This is a second number. "
}
}
]
}
] ,
" activeSignature " : 0 ,
" activeParameter " : 1
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_actions ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client
. write_request ( " textDocument/codeAction " ,
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 7 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 7 }
} ,
" severity " : 1 ,
" code " : 1308 ,
" source " : " deno-ts " ,
" message " : " 'await' expressions are only allowed within async functions and at the top levels of modules. " ,
" relatedInformation " : [ ]
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
2023-01-12 20:59:13 -05:00
)
2023-03-09 15:09:03 -05:00
;
2023-03-08 18:15:20 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" title " : " Add async modifier to containing function " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 7 }
} ,
" severity " : 1 ,
" code " : 1308 ,
" source " : " deno-ts " ,
" message " : " 'await' expressions are only allowed within async functions and at the top levels of modules. " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" newText " : " async "
} , {
" range " : {
" start " : { " line " : 0 , " character " : 21 } ,
" end " : { " line " : 0 , " character " : 25 }
} ,
" newText " : " Promise<void> "
} ]
} ]
}
2023-09-18 12:39:28 -04:00
} , {
" title " : " Add all missing 'async' modifiers " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 7 }
} ,
" severity " : 1 ,
" code " : 1308 ,
" source " : " deno-ts " ,
" message " : " 'await' expressions are only allowed within async functions and at the top levels of modules. " ,
" relatedInformation " : [ ]
} ] ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" fixId " : " fixAwaitInSyncFunction "
}
2023-03-09 15:09:03 -05:00
} ] )
2023-03-08 18:15:20 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client
. write_request ( " codeAction/resolve " ,
2023-03-08 18:15:20 -05:00
json! ( {
" title " : " Add all missing 'async' modifiers " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 2 } ,
" end " : { " line " : 1 , " character " : 7 }
} ,
" severity " : 1 ,
" code " : 1308 ,
" source " : " deno-ts " ,
" message " : " 'await' expressions are only allowed within async functions and at the top levels of modules. " ,
" relatedInformation " : [ ]
} ] ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" fixId " : " fixAwaitInSyncFunction "
}
} ) ,
2023-01-12 20:59:13 -05:00
)
2023-03-09 15:09:03 -05:00
;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" title " : " Add all missing 'async' modifiers " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : {
" line " : 1 ,
" character " : 2
} ,
" end " : {
" line " : 1 ,
" character " : 7
}
} ,
" severity " : 1 ,
" code " : 1308 ,
" source " : " deno-ts " ,
" message " : " 'await' expressions are only allowed within async functions and at the top levels of modules. " ,
" relatedInformation " : [ ]
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" newText " : " async "
} , {
" range " : {
" start " : { " line " : 0 , " character " : 21 } ,
" end " : { " line " : 0 , " character " : 25 }
} ,
" newText " : " Promise<void> "
} , {
" range " : {
" start " : { " line " : 4 , " character " : 7 } ,
" end " : { " line " : 4 , " character " : 7 }
} ,
" newText " : " async "
} , {
" range " : {
" start " : { " line " : 4 , " character " : 21 } ,
" end " : { " line " : 4 , " character " : 25 }
} ,
" newText " : " Promise<void> "
} ]
} ]
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" fixId " : " fixAwaitInSyncFunction "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-09-17 14:05:19 -04:00
#[ test ]
fn test_lsp_code_actions_ordering ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #"
import " https://deno.land/x/a/mod.ts " ;
let a = " a " ;
console . log ( a ) ;
export function b ( ) : void {
await Promise . resolve ( " b " ) ;
}
" #
}
} ) ) ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 1 , " character " : 11 } ,
" end " : { " line " : 6 , " character " : 12 }
} ,
" context " : {
" diagnostics " : diagnostics . all ( ) ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
// Simplify the serialization to `{ title, source }` for this test.
let mut actions : Vec < Value > = serde_json ::from_value ( res ) . unwrap ( ) ;
for action in & mut actions {
let action = action . as_object_mut ( ) . unwrap ( ) ;
let title = action . get ( " title " ) . unwrap ( ) . as_str ( ) . unwrap ( ) . to_string ( ) ;
let diagnostics = action . get ( " diagnostics " ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
let diagnostic = diagnostics . get ( 0 ) . unwrap ( ) . as_object ( ) . unwrap ( ) ;
let source = diagnostic . get ( " source " ) . unwrap ( ) ;
let source = source . as_str ( ) . unwrap ( ) . to_string ( ) ;
action . clear ( ) ;
action . insert ( " title " . to_string ( ) , serde_json ::to_value ( title ) . unwrap ( ) ) ;
action . insert ( " source " . to_string ( ) , serde_json ::to_value ( source ) . unwrap ( ) ) ;
}
let res = serde_json ::to_value ( actions ) . unwrap ( ) ;
// Ensure ordering is "deno-ts" -> "deno" -> "deno-lint".
assert_eq! (
res ,
json! ( [
{
" title " : " Add async modifier to containing function " ,
" source " : " deno-ts " ,
} ,
{
" title " : " Cache \" https://deno.land/x/a/mod.ts \" and its dependencies. " ,
" source " : " deno " ,
} ,
{
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for this line " ,
2023-09-17 14:05:19 -04:00
" source " : " deno-lint " ,
} ,
{
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for the entire file " ,
2023-09-17 14:05:19 -04:00
" source " : " deno-lint " ,
} ,
{
2023-09-18 12:39:28 -04:00
" title " : " Ignore lint errors for the entire file " ,
2023-09-17 14:05:19 -04:00
" source " : " deno-lint " ,
} ,
{
2023-09-18 12:39:28 -04:00
" title " : " Disable no-await-in-sync-fn for this line " ,
2023-09-17 14:05:19 -04:00
" source " : " deno-lint " ,
} ,
{
2023-09-18 12:39:28 -04:00
" title " : " Disable no-await-in-sync-fn for the entire file " ,
2023-09-17 14:05:19 -04:00
" source " : " deno-lint " ,
} ,
{
" title " : " Ignore lint errors for the entire file " ,
" source " : " deno-lint " ,
} ,
] )
) ;
}
2023-09-26 20:48:34 -04:00
#[ test ]
fn lsp_status_file ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let res = client . write_request (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/status.md "
}
} ) ,
) ;
let res = res . as_str ( ) . unwrap ( ) . to_string ( ) ;
assert! ( res . starts_with ( " # Deno Language Server Status " ) ) ;
let res = client . write_request (
" deno/virtualTextDocument " ,
json! ( {
" textDocument " : {
" uri " : " deno:/status.md?1 "
}
} ) ,
) ;
let res = res . as_str ( ) . unwrap ( ) . to_string ( ) ;
assert! ( res . starts_with ( " # Deno Language Server Status " ) ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_code_actions_deno_cache ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
2023-01-12 20:59:13 -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 "
}
} ) ) ;
assert_eq! (
2023-05-26 02:10:18 -04:00
diagnostics . messages_with_source ( " deno " ) ,
2023-03-08 18:15:20 -05:00
serde_json ::from_value ( json! ( {
" uri " : " file:///a/file.ts " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
2023-07-10 22:27:22 -04:00
" message " : " Uncached or missing remote URL: https://deno.land/x/a/mod.ts " ,
2023-03-08 18:15:20 -05:00
" data " : { " specifier " : " https://deno.land/x/a/mod.ts " }
} ] ,
" version " : 1
} ) ) . unwrap ( )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res =
client
. write_request ( " textDocument/codeAction " ,
2023-03-08 18:15:20 -05:00
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 49 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Unable to load the remote module: \" https://deno.land/x/a/mod.ts \" . " ,
" data " : {
" specifier " : " https://deno.land/x/a/mod.ts "
}
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
2023-01-12 20:59:13 -05:00
)
2023-03-09 15:09:03 -05:00
;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" title " : " Cache \" https://deno.land/x/a/mod.ts \" and its dependencies. " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Unable to load the remote module: \" https://deno.land/x/a/mod.ts \" . " ,
" data " : {
" specifier " : " https://deno.land/x/a/mod.ts "
}
} ] ,
" command " : {
" title " : " " ,
" command " : " deno.cache " ,
2023-09-05 11:36:35 -04:00
" arguments " : [ [ " https://deno.land/x/a/mod.ts " ] , " file:///a/file.ts " ]
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_actions_deno_cache_npm ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
2023-01-12 20:59:13 -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 "
}
} ) ) ;
assert_eq! (
2023-05-26 02:10:18 -04:00
diagnostics . messages_with_source ( " deno " ) ,
2023-03-08 18:15:20 -05:00
serde_json ::from_value ( json! ( {
" uri " : " file:///a/file.ts " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 18 } ,
" end " : { " line " : 0 , " character " : 29 }
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
2023-07-10 22:27:22 -04:00
" message " : " Uncached or missing npm package: chalk " ,
2023-03-08 18:15:20 -05:00
" data " : { " specifier " : " npm:chalk " }
} ] ,
" version " : 1
} ) )
. unwrap ( )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 18 } ,
" end " : { " line " : 0 , " character " : 29 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 18 } ,
" end " : { " line " : 0 , " character " : 29 }
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
2023-07-10 22:27:22 -04:00
" message " : " Uncached or missing npm package: chalk " ,
2023-03-09 15:09:03 -05:00
" data " : { " specifier " : " npm:chalk " }
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" title " : " Cache \" npm:chalk \" and its dependencies. " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 18 } ,
" end " : { " line " : 0 , " character " : 29 }
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
2023-07-10 22:27:22 -04:00
" message " : " Uncached or missing npm package: chalk " ,
2023-03-08 18:15:20 -05:00
" data " : { " specifier " : " npm:chalk " }
} ] ,
" command " : {
" title " : " " ,
" command " : " deno.cache " ,
2023-09-05 11:36:35 -04:00
" arguments " : [ [ " npm:chalk " ] , " file:///a/file.ts " ]
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-09-24 18:33:52 -04:00
#[ test ]
fn lsp_code_actions_deno_cache_all ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #"
import * as a from " https://deno.land/x/a/mod.ts " ;
import chalk from " npm:chalk " ;
console . log ( a ) ;
console . log ( chalk ) ;
" #,
}
} ) ) ;
assert_eq! (
diagnostics . messages_with_source ( " deno " ) ,
serde_json ::from_value ( json! ( {
" uri " : " file:///a/file.ts " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 1 , " character " : 27 } ,
" end " : { " line " : 1 , " character " : 57 } ,
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Uncached or missing remote URL: https://deno.land/x/a/mod.ts " ,
" data " : { " specifier " : " https://deno.land/x/a/mod.ts " } ,
} ,
{
" range " : {
" start " : { " line " : 2 , " character " : 26 } ,
" end " : { " line " : 2 , " character " : 37 } ,
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
" message " : " Uncached or missing npm package: chalk " ,
" data " : { " specifier " : " npm:chalk " } ,
} ,
] ,
" version " : 1 ,
} ) ) . unwrap ( )
) ;
let res =
client
. write_request ( " textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" range " : {
" start " : { " line " : 1 , " character " : 27 } ,
" end " : { " line " : 1 , " character " : 57 } ,
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 27 } ,
" end " : { " line " : 1 , " character " : 57 } ,
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Uncached or missing remote URL: https://deno.land/x/a/mod.ts " ,
" data " : {
" specifier " : " https://deno.land/x/a/mod.ts " ,
} ,
} ] ,
" only " : [ " quickfix " ] ,
}
} ) ,
)
;
assert_eq! (
res ,
json! ( [
{
" title " : " Cache \" https://deno.land/x/a/mod.ts \" and its dependencies. " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 27 } ,
" end " : { " line " : 1 , " character " : 57 } ,
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Uncached or missing remote URL: https://deno.land/x/a/mod.ts " ,
" data " : {
" specifier " : " https://deno.land/x/a/mod.ts " ,
} ,
} ] ,
" command " : {
" title " : " " ,
" command " : " deno.cache " ,
" arguments " : [ [ " https://deno.land/x/a/mod.ts " ] , " file:///a/file.ts " ] ,
}
} ,
{
" title " : " Cache all dependencies of this module. " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 1 , " character " : 27 } ,
" end " : { " line " : 1 , " character " : 57 } ,
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Uncached or missing remote URL: https://deno.land/x/a/mod.ts " ,
" data " : {
" specifier " : " https://deno.land/x/a/mod.ts " ,
} ,
} ,
{
" range " : {
" start " : { " line " : 2 , " character " : 26 } ,
" end " : { " line " : 2 , " character " : 37 } ,
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
" message " : " Uncached or missing npm package: chalk " ,
" data " : { " specifier " : " npm:chalk " } ,
} ,
] ,
" command " : {
" title " : " " ,
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
}
} ,
] )
) ;
client . shutdown ( ) ;
}
2023-09-24 12:59:42 -04:00
#[ test ]
fn lsp_cache_on_save ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write (
" file.ts " ,
r #"
import { printHello } from " http://localhost:4545/subdir/print_hello.ts " ;
printHello ( ) ;
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( {
2023-09-24 12:59:42 -04:00
" deno " : {
" enable " : true ,
" cacheOnSave " : true ,
} ,
2023-10-24 16:27:27 -04:00
} ) ) ;
2023-09-24 12:59:42 -04:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " file.ts " ) ,
}
} ) ) ;
assert_eq! (
diagnostics . messages_with_source ( " deno " ) ,
serde_json ::from_value ( json! ( {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 33 } ,
" end " : { " line " : 1 , " character " : 78 }
} ,
" severity " : 1 ,
" code " : " no-cache " ,
" source " : " deno " ,
" message " : " Uncached or missing remote URL: http://localhost:4545/subdir/print_hello.ts " ,
" data " : { " specifier " : " http://localhost:4545/subdir/print_hello.ts " }
} ] ,
" version " : 1
} ) )
. unwrap ( )
) ;
client . did_save ( json! ( {
" textDocument " : { " uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) } ,
} ) ) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) , vec! [ ] ) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_code_actions_imports ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file00.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export interface MallardDuckConfigOptions extends DuckConfigOptions {
kind : " mallard " ;
}
export class MallardDuckConfig extends DuckConfig {
constructor ( options : MallardDuckConfigOptions ) {
super ( options ) ;
}
}
" #
}
} ) ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" import { DuckConfigOptions } from " . / file02 . ts " ;
export class DuckConfig {
readonly kind ;
constructor ( options : DuckConfigOptions ) {
this . kind = options . kind ;
}
}
" #
}
} ) ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file02.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export interface DuckConfigOptions {
kind : string ;
quacks : boolean ;
}
" #
}
} ) ) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file00.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 6 , " character " : 0 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 50 } ,
" end " : { " line " : 0 , " character " : 67 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} , {
" range " : {
" start " : { " line " : 4 , " character " : 39 } ,
" end " : { " line " : 4 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfig'. "
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-09-18 12:39:28 -04:00
" title " : " Add import from \" ./file02.ts \" " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 50 } ,
" end " : { " line " : 0 , " character " : 67 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
2023-09-18 12:39:28 -04:00
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file00.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { DuckConfigOptions } from \" ./file02.ts \" ; \n \n "
} ]
} ]
2023-03-08 18:15:20 -05:00
}
} , {
" title " : " Add import from \" ./file01.ts \" " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 4 , " character " : 39 } ,
" end " : { " line " : 4 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfig'. "
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file00.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { DuckConfig } from \" ./file01.ts \" ; \n \n "
} ]
} ]
}
2023-03-21 11:46:40 -04:00
} , {
2023-09-18 12:39:28 -04:00
" title " : " Add all missing imports " ,
2023-03-21 11:46:40 -04:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 50 } ,
" end " : { " line " : 0 , " character " : 67 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
2023-09-18 12:39:28 -04:00
" data " : {
" specifier " : " file:///a/file00.ts " ,
" fixId " : " fixMissingImport "
2023-03-21 11:46:40 -04:00
}
2023-03-09 15:09:03 -05:00
} ] )
) ;
let res = client . write_request (
" codeAction/resolve " ,
json! ( {
" title " : " Add all missing imports " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 50 } ,
" end " : { " line " : 0 , " character " : 67 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} , {
" range " : {
" start " : { " line " : 4 , " character " : 39 } ,
" end " : { " line " : 4 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfig'. "
} ] ,
" data " : {
" specifier " : " file:///a/file00.ts " ,
" fixId " : " fixMissingImport "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" title " : " Add all missing imports " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 50 } ,
" end " : { " line " : 0 , " character " : 67 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} , {
" range " : {
" start " : { " line " : 4 , " character " : 39 } ,
" end " : { " line " : 4 , " character " : 49 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfig'. "
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file00.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { DuckConfig } from \" ./file01.ts \" ; \n import { DuckConfigOptions } from \" ./file02.ts \" ; \n \n "
} ]
} ]
} ,
" data " : {
" specifier " : " file:///a/file00.ts " ,
" fixId " : " fixMissingImport "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_actions_refactor ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " var x: { a?: number; b?: string } = {}; \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/codeAction " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" context " : {
" diagnostics " : [ ] ,
" only " : [ " refactor " ]
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-21 11:46:40 -04:00
" title " : " Move to a new file " ,
" kind " : " refactor.move.newFile " ,
2023-03-08 18:15:20 -05:00
" isPreferred " : false ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
2023-03-21 11:46:40 -04:00
" refactorName " : " Move to a new file " ,
" actionName " : " Move to a new file "
2023-03-08 18:15:20 -05:00
}
} , {
2023-03-21 11:46:40 -04:00
" title " : " Extract to function in module scope " ,
" kind " : " refactor.extract.function " ,
2023-03-08 18:15:20 -05:00
" isPreferred " : false ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Extract Symbol " ,
2023-03-21 11:46:40 -04:00
" actionName " : " function_scope_0 "
2023-03-08 18:15:20 -05:00
}
} , {
2023-03-21 11:46:40 -04:00
" title " : " Extract to constant in enclosing scope " ,
" kind " : " refactor.extract.constant " ,
2023-03-08 18:15:20 -05:00
" isPreferred " : false ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
2023-03-21 11:46:40 -04:00
" refactorName " : " Extract Symbol " ,
" actionName " : " constant_scope_0 "
2023-03-08 18:15:20 -05:00
}
} , {
" title " : " Convert default export to named export " ,
" kind " : " refactor.rewrite.export.named " ,
" isPreferred " : false ,
" disabled " : {
" reason " : " This file already has a default export "
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Convert export " ,
" actionName " : " Convert default export to named export "
}
} , {
" title " : " Convert named export to default export " ,
" kind " : " refactor.rewrite.export.default " ,
" isPreferred " : false ,
" disabled " : {
" reason " : " This file already has a default export "
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Convert export " ,
" actionName " : " Convert named export to default export "
}
} , {
" title " : " Convert namespace import to named imports " ,
" kind " : " refactor.rewrite.import.named " ,
" isPreferred " : false ,
" disabled " : {
" reason " : " Selection is not an import declaration. "
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Convert import " ,
" actionName " : " Convert namespace import to named imports "
}
} , {
" title " : " Convert named imports to default import " ,
" kind " : " refactor.rewrite.import.default " ,
" isPreferred " : false ,
" disabled " : {
" reason " : " Selection is not an import declaration. "
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Convert import " ,
" actionName " : " Convert named imports to default import "
}
} , {
" title " : " Convert named imports to namespace import " ,
" kind " : " refactor.rewrite.import.namespace " ,
" isPreferred " : false ,
" disabled " : {
" reason " : " Selection is not an import declaration. "
} ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
} ,
" refactorName " : " Convert import " ,
" actionName " : " Convert named imports to namespace import "
}
2023-03-09 15:09:03 -05:00
} ] )
) ;
let res = client . write_request (
" codeAction/resolve " ,
json! ( {
" title " : " Extract to interface " ,
" kind " : " refactor.extract.interface " ,
" isPreferred " : true ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 33 }
} ,
" refactorName " : " Extract type " ,
" actionName " : " Extract to interface "
}
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" title " : " Extract to interface " ,
" kind " : " refactor.extract.interface " ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " interface NewType { \n a?: number; \n b?: string; \n } \n \n "
} , {
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 33 }
} ,
" newText " : " NewType "
} ]
} ]
} ,
" isPreferred " : true ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 33 }
} ,
" refactorName " : " Extract type " ,
" actionName " : " Extract to interface "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-09-18 15:48:32 -04:00
#[ test ]
fn lsp_code_actions_imports_respects_fmt_config ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write (
" ./deno.jsonc " ,
json! ( {
" fmt " : {
" semiColons " : false ,
" singleQuote " : true ,
}
} )
. to_string ( ) ,
) ;
temp_dir . write (
" file00.ts " ,
r #"
export interface MallardDuckConfigOptions extends DuckConfigOptions {
kind : " mallard " ;
}
" #,
) ;
temp_dir . write (
" file01.ts " ,
r #"
export interface DuckConfigOptions {
kind : string ;
quacks : boolean ;
}
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " file00.ts " ) ,
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file01.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " file01.ts " ) ,
}
} ) ) ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( )
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 4 , " character " : 0 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 55 } ,
" end " : { " line " : 1 , " character " : 64 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" ./file01.ts \" " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 55 } ,
" end " : { " line " : 1 , " character " : 64 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { DuckConfigOptions } from './file01.ts' \n "
} ]
} ]
}
} ] )
) ;
let res = client . write_request (
" codeAction/resolve " ,
json! ( {
" title " : " Add all missing imports " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 55 } ,
" end " : { " line " : 1 , " character " : 64 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
" data " : {
" specifier " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" fixId " : " fixMissingImport "
}
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" title " : " Add all missing imports " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 55 } ,
" end " : { " line " : 1 , " character " : 64 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. "
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" version " : 1
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { DuckConfigOptions } from './file01.ts' \n "
} ]
} ]
} ,
" data " : {
" specifier " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" fixId " : " fixMissingImport "
}
} )
) ;
client . shutdown ( ) ;
}
2023-10-16 21:51:42 -04:00
#[ test ]
fn lsp_quote_style_from_workspace_settings ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write (
" file00.ts " ,
r #"
export interface MallardDuckConfigOptions extends DuckConfigOptions {
kind : " mallard " ;
}
" #,
) ;
temp_dir . write (
" file01.ts " ,
r #"
export interface DuckConfigOptions {
kind : string ;
quacks : boolean ;
}
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( {
" deno " : {
" enable " : true ,
} ,
2023-10-16 21:51:42 -04:00
" typescript " : {
" preferences " : {
" quoteStyle " : " single " ,
} ,
} ,
2023-10-24 16:27:27 -04:00
} ) ) ;
2023-10-16 21:51:42 -04:00
let code_action_params = json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 4 , " character " : 0 } ,
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 56 } ,
" end " : { " line " : 1 , " character " : 73 } ,
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. " ,
} ] ,
" only " : [ " quickfix " ] ,
} ,
} ) ;
let res =
client . write_request ( " textDocument/codeAction " , code_action_params . clone ( ) ) ;
// Expect single quotes in the auto-import.
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" ./file01.ts \" " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 56 } ,
" end " : { " line " : 1 , " character " : 73 } ,
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. " ,
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" version " : null ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 } ,
} ,
" newText " : " import { DuckConfigOptions } from './file01.ts'; \n " ,
} ] ,
} ] ,
} ,
} ] ) ,
) ;
// It should ignore the workspace setting if a `deno.json` is present.
temp_dir . write ( " ./deno.json " , json! ( { } ) . to_string ( ) ) ;
client . did_change_watched_files ( json! ( {
" changes " : [ {
" uri " : temp_dir . uri ( ) . join ( " deno.json " ) . unwrap ( ) ,
" type " : 1 ,
} ] ,
} ) ) ;
let res = client . write_request ( " textDocument/codeAction " , code_action_params ) ;
// Expect double quotes in the auto-import.
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" ./file01.ts \" " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 56 } ,
" end " : { " line " : 1 , " character " : 73 } ,
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'DuckConfigOptions'. " ,
} ] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file00.ts " ) . unwrap ( ) ,
" version " : null ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 } ,
} ,
" newText " : " import { DuckConfigOptions } from \" ./file01.ts \" ; \n " ,
} ] ,
} ] ,
} ,
} ] ) ,
) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_code_actions_refactor_no_disabled_support ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder . with_capabilities ( | c | {
let doc = c . text_document . as_mut ( ) . unwrap ( ) ;
let code_action = doc . code_action . as_mut ( ) . unwrap ( ) ;
code_action . disabled_support = Some ( false ) ;
} ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = 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 " ]
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-21 11:46:40 -04:00
" title " : " Move to a new file " ,
" kind " : " refactor.move.newFile " ,
2023-03-08 18:15:20 -05:00
" isPreferred " : false ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 14 , " character " : 0 }
} ,
2023-03-21 11:46:40 -04:00
" refactorName " : " Move to a new file " ,
" actionName " : " Move to a new file "
2023-03-08 18:15:20 -05:00
}
} , {
2023-03-21 11:46:40 -04:00
" title " : " Extract to function in module scope " ,
" kind " : " refactor.extract.function " ,
2023-03-08 18:15:20 -05:00
" isPreferred " : false ,
" data " : {
" specifier " : " file:///a/file.ts " ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 14 , " character " : 0 }
} ,
2023-03-21 11:46:40 -04:00
" refactorName " : " Extract Symbol " ,
" actionName " : " function_scope_0 "
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_actions_deadlock ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
let large_file_text =
fs ::read_to_string ( testdata_path ( ) . join ( " lsp " ) . join ( " large_file.txt " ) )
. unwrap ( ) ;
2023-10-24 16:27:27 -04:00
client . did_open ( json! ( {
2023-03-09 15:09:03 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " javascript " ,
" version " : 1 ,
" text " : large_file_text ,
}
} ) ) ;
client . write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
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 " : " +++ "
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
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 " : " // "
}
]
} ) ,
) ;
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 "
}
]
} ) ,
) ;
2023-01-12 20:59:13 -05:00
// 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 ) ) ;
2023-03-09 15:09:03 -05:00
client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 609 , " character " : 33 , }
} ) ,
) ;
client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 441 , " character " : 33 } ,
" end " : { " line " : 441 , " character " : 42 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 441 , " character " : 33 } ,
" end " : { " line " : 441 , " character " : 42 }
} ,
" severity " : 1 ,
" code " : 7031 ,
" source " : " deno-ts " ,
" message " : " Binding element 'debugFlag' implicitly has an 'any' type. "
} ] ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . read_diagnostics ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " Deno. "
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 0 , 5 ) ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" triggerKind " : 2 ,
" triggerCharacter " : " . "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert! ( ! list . is_incomplete ) ;
assert! ( list . items . len ( ) > 90 ) ;
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " build " ,
" kind " : 6 ,
" sortText " : " 1 " ,
" insertTextFormat " : 1 ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 5 ,
" name " : " build " ,
" useCodeSnippet " : false
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" label " : " build " ,
" kind " : 6 ,
" detail " : " const Deno.build: { \n target: string; \n arch: \" x86_64 \" | \" aarch64 \" ; \n os: \" darwin \" | \" linux \" | \" windows \" | \" freebsd \" | \" netbsd \" | \" aix \" | \" solaris \" | \" illumos \" ; \n vendor: string; \n env?: string | undefined; \n } " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " Information related to the build of the current Deno runtime. \n \n Users are discouraged from code branching based on this information, as \n assumptions about what is available in what build environment might change \n over time. Developers should specifically sniff out the features they \n intend to use. \n \n The intended use for the information is for logging and debugging purposes. \n \n *@category* - Runtime Environment "
} ,
" sortText " : " 1 " ,
" insertTextFormat " : 1
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions_private_fields ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" class Foo { #myProperty = " value " ; constructor() { this.# } } " #
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 0 , 57 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert_eq! ( list . items . len ( ) , 1 ) ;
let item = & list . items [ 0 ] ;
assert_eq! ( item . label , " #myProperty " ) ;
assert! ( ! list . is_incomplete ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions_optional ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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.) "
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . get_completion (
" file:///a/file.ts " ,
( 8 , 4 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
json! ( res ) ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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
}
}
}
]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " b? " ,
" kind " : 5 ,
" sortText " : " 1 " ,
" filterText " : " b " ,
" insertText " : " b " ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 79 ,
" name " : " b " ,
" useCodeSnippet " : false
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" label " : " b? " ,
" kind " : 5 ,
" detail " : " (property) A.b?: string | undefined " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 1 " ,
" filterText " : " b " ,
" insertText " : " b "
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions_auto_import ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const foo = \" foo \" ; \n " ,
}
} ) ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export {}; \n \n " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 2 , 0 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
2023-03-28 17:58:12 -04:00
let item = list . items . iter ( ) . find ( | item | item . label = = " foo " ) ;
2023-07-31 21:31:30 -04:00
let Some ( item ) = item else {
2023-03-09 15:09:03 -05:00
panic! ( " completions items missing 'foo' symbol " ) ;
2023-07-31 21:31:30 -04:00
} ;
let mut item_value = serde_json ::to_value ( item ) . unwrap ( ) ;
item_value [ " data " ] [ " tsc " ] [ " data " ] [ " exportMapKey " ] =
serde_json ::Value ::String ( " " . to_string ( ) ) ;
2023-03-09 15:09:03 -05:00
2023-03-28 17:58:12 -04:00
let req = json! ( {
" label " : " foo " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " ./b.ts " ,
} ,
2023-03-28 17:58:12 -04:00
" kind " : 6 ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_0 " ,
2023-03-28 17:58:12 -04:00
" commitCharacters " : [
" . " ,
" , " ,
" ; " ,
" ( "
] ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 12 ,
" name " : " foo " ,
" source " : " ./b.ts " ,
" data " : {
" exportName " : " foo " ,
2023-07-31 21:31:30 -04:00
" exportMapKey " : " " ,
2023-03-28 17:58:12 -04:00
" moduleSpecifier " : " ./b.ts " ,
" fileName " : " file:///a/b.ts "
} ,
" useCodeSnippet " : false
2023-01-12 20:59:13 -05:00
}
2023-03-28 17:58:12 -04:00
}
} ) ;
2023-07-31 21:31:30 -04:00
assert_eq! ( item_value , req ) ;
2023-03-28 17:58:12 -04:00
let res = client . write_request ( " completionItem/resolve " , req ) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" label " : " foo " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " ./b.ts " ,
} ,
2023-01-12 20:59:13 -05:00
" kind " : 6 ,
" detail " : " const foo: \" foo \" " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_0 " ,
2023-01-12 20:59:13 -05:00
" additionalTextEdits " : [
{
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " import { foo } from \" ./b.ts \" ; \n \n "
}
]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
}
2023-07-01 21:07:57 -04:00
#[ test ]
2023-07-03 14:09:24 -04:00
fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map ( ) {
2023-07-01 21:07:57 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open (
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import {getClient} from 'npm:@denotest/types-exports-subpaths@1/client';import chalk from 'npm:chalk@5.0'; \n \n " ,
}
} ) ,
) ;
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-07-01 21:07:57 -04:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " npm:@denotest/types-exports-subpaths@1/client " , " npm:chalk@5.0 " ] ,
" file:///a/file.ts " ,
] ,
2023-07-01 21:07:57 -04:00
} ) ,
) ;
// try auto-import with path
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/a.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " getClie " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/a.ts " ,
( 0 , 7 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let item = list
. items
. iter ( )
. find ( | item | item . label = = " getClient " )
. unwrap ( ) ;
let res = client . write_request ( " completionItem/resolve " , item ) ;
assert_eq! (
res ,
json! ( {
" label " : " getClient " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " npm:@denotest/types-exports-subpaths@1/client " ,
} ,
2023-07-01 21:07:57 -04:00
" kind " : 3 ,
" detail " : " function getClient(): 5 " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_1 " ,
2023-07-01 21:07:57 -04:00
" additionalTextEdits " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { getClient } from \" npm:@denotest/types-exports-subpaths@1/client \" ; \n \n "
}
]
} )
) ;
// try quick fix with path
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " getClient " ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( " file:///a/b.ts " , " deno-ts " )
. diagnostics ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 9 }
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [ " quickfix " ]
}
} ) ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" npm:@denotest/types-exports-subpaths@1/client \" " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 9 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'getClient'. " ,
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { getClient } from \" npm:@denotest/types-exports-subpaths@1/client \" ; \n \n "
} ]
} ]
}
} ] )
) ;
// try auto-import without path
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/c.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " chal " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/c.ts " ,
( 0 , 4 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let item = list
. items
. iter ( )
. find ( | item | item . label = = " chalk " )
. unwrap ( ) ;
let mut res = client . write_request ( " completionItem/resolve " , item ) ;
let obj = res . as_object_mut ( ) . unwrap ( ) ;
obj . remove ( " detail " ) ; // not worth testing these
obj . remove ( " documentation " ) ;
assert_eq! (
res ,
json! ( {
" label " : " chalk " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " npm:chalk@5.0 " ,
} ,
2023-07-01 21:07:57 -04:00
" kind " : 6 ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_1 " ,
2023-07-01 21:07:57 -04:00
" additionalTextEdits " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import chalk from \" npm:chalk@5.0 \" ; \n \n "
}
]
} )
) ;
// try quick fix without path
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/d.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " chalk " ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( " file:///a/d.ts " , " deno-ts " )
. diagnostics ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/d.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 5 }
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [ " quickfix " ]
}
} ) ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" npm:chalk@5.0 \" " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 5 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'chalk'. " ,
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/d.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import chalk from \" npm:chalk@5.0 \" ; \n \n "
} ]
} ]
}
} ] )
) ;
}
2023-07-03 14:09:24 -04:00
#[ test ]
2023-09-10 15:09:45 -04:00
fn lsp_semantic_tokens_for_disabled_module ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_with_config (
| builder | {
builder . set_deno_enable ( false ) ;
} ,
2023-09-21 01:46:39 -04:00
json! ( { " deno " : {
2023-09-10 15:09:45 -04:00
" enable " : false
2023-09-21 01:46:39 -04:00
} } ) ,
2023-09-10 15:09:45 -04:00
) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const someConst = 1; someConst "
}
} ) ) ;
let res = client . write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" data " : [ 0 , 6 , 9 , 7 , 9 , 0 , 15 , 9 , 7 , 8 ] ,
} )
) ;
}
#[ test ]
2023-07-03 14:09:24 -04:00
fn lsp_completions_auto_import_and_quick_fix_with_import_map ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
let import_map = r #" {
" imports " : {
" print_hello " : " http://localhost:4545/subdir/print_hello.ts " ,
" chalk " : " npm:chalk@~5 " ,
" types-exports-subpaths/ " : " npm:/@denotest/types-exports-subpaths@1/ "
}
} " #;
temp_dir . write ( " import_map.json " , import_map ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_import_map ( " import_map.json " ) ;
} ) ;
client . did_open (
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : concat ! (
" import {getClient} from 'npm:@denotest/types-exports-subpaths@1/client'; \n " ,
" import _test1 from 'npm:chalk@^5.0'; \n " ,
" import chalk from 'npm:chalk@~5'; \n " ,
" import chalk from 'npm:chalk@~5'; \n " ,
" import {printHello} from 'print_hello'; \n " ,
" \n " ,
) ,
}
} ) ,
) ;
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-07-03 14:09:24 -04:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[
" npm:@denotest/types-exports-subpaths@1/client " ,
" npm:chalk@^5.0 " ,
" npm:chalk@~5 " ,
" http://localhost:4545/subdir/print_hello.ts " ,
] ,
" file:///a/file.ts " ,
] ,
2023-07-03 14:09:24 -04:00
} ) ,
) ;
// try auto-import with path
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/a.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " getClie " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/a.ts " ,
( 0 , 7 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let item = list
. items
. iter ( )
. find ( | item | item . label = = " getClient " )
. unwrap ( ) ;
let res = client . write_request ( " completionItem/resolve " , item ) ;
assert_eq! (
res ,
json! ( {
" label " : " getClient " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " types-exports-subpaths/client " ,
} ,
2023-07-03 14:09:24 -04:00
" kind " : 3 ,
" detail " : " function getClient(): 5 " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_0 " ,
2023-07-03 14:09:24 -04:00
" additionalTextEdits " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { getClient } from \" types-exports-subpaths/client \" ; \n \n "
}
]
} )
) ;
// try quick fix with path
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " getClient " ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( " file:///a/b.ts " , " deno-ts " )
. diagnostics ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/b.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 9 }
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [ " quickfix " ]
}
} ) ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" types-exports-subpaths/client \" " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 9 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'getClient'. " ,
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/b.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { getClient } from \" types-exports-subpaths/client \" ; \n \n "
} ]
} ]
}
} ] )
) ;
// try auto-import without path
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/c.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " chal " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/c.ts " ,
( 0 , 4 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let item = list
. items
. iter ( )
. find ( | item | item . label = = " chalk " )
. unwrap ( ) ;
let mut res = client . write_request ( " completionItem/resolve " , item ) ;
let obj = res . as_object_mut ( ) . unwrap ( ) ;
obj . remove ( " detail " ) ; // not worth testing these
obj . remove ( " documentation " ) ;
assert_eq! (
res ,
json! ( {
" label " : " chalk " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " chalk " ,
} ,
2023-07-03 14:09:24 -04:00
" kind " : 6 ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_0 " ,
2023-07-03 14:09:24 -04:00
" additionalTextEdits " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import chalk from \" chalk \" ; \n \n "
}
]
} )
) ;
// try quick fix without path
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/d.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " chalk " ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( " file:///a/d.ts " , " deno-ts " )
. diagnostics ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/d.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 5 }
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [ " quickfix " ]
}
} ) ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" chalk \" " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 5 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'chalk'. " ,
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/d.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import chalk from \" chalk \" ; \n \n "
} ]
} ]
}
} ] )
) ;
// try auto-import with http import map
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/e.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " printH " ,
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/e.ts " ,
( 0 , 6 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let item = list
. items
. iter ( )
. find ( | item | item . label = = " printHello " )
. unwrap ( ) ;
let mut res = client . write_request ( " completionItem/resolve " , item ) ;
let obj = res . as_object_mut ( ) . unwrap ( ) ;
obj . remove ( " detail " ) ; // not worth testing these
obj . remove ( " documentation " ) ;
assert_eq! (
res ,
json! ( {
" label " : " printHello " ,
2023-09-16 10:51:35 -04:00
" labelDetails " : {
" description " : " print_hello " ,
} ,
2023-07-03 14:09:24 -04:00
" kind " : 3 ,
2023-09-18 14:55:24 -04:00
" sortText " : " 16_0 " ,
2023-07-03 14:09:24 -04:00
" additionalTextEdits " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { printHello } from \" print_hello \" ; \n \n "
}
]
} )
) ;
// try quick fix with http import
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/f.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " printHello " ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( " file:///a/f.ts " , " deno-ts " )
. diagnostics ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( json! ( {
" textDocument " : {
" uri " : " file:///a/f.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 10 }
} ,
" context " : {
" diagnostics " : diagnostics ,
" only " : [ " quickfix " ]
}
} ) ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Add import from \" print_hello \" " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 10 }
} ,
" severity " : 1 ,
" code " : 2304 ,
" source " : " deno-ts " ,
" message " : " Cannot find name 'printHello'. " ,
}
] ,
" edit " : {
" documentChanges " : [ {
" textDocument " : {
" uri " : " file:///a/f.ts " ,
" version " : 1 ,
} ,
" edits " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " import { printHello } from \" print_hello \" ; \n \n "
} ]
} ]
}
} ] )
) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_completions_snippet ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
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 list = client . get_completion_list (
" file:///a/a.tsx " ,
( 5 , 13 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
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
}
}
}
]
} )
) ;
let res = client . write_request (
" completionItem/resolve " ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" 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
}
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" label " : " type " ,
" kind " : 5 ,
" detail " : " (property) type: string " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " "
} ,
" sortText " : " 11 " ,
" filterText " : " type= \" $1 \" " ,
" insertText " : " type= \" $1 \" " ,
" insertTextFormat " : 2
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
}
#[ test ]
fn lsp_completions_no_snippet ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize ( | builder | {
builder . with_capabilities ( | c | {
let doc = c . text_document . as_mut ( ) . unwrap ( ) ;
doc . completion = None ;
} ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 } " ,
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let list = client . get_completion_list (
" file:///a/a.tsx " ,
( 5 , 13 ) ,
json! ( { " triggerKind " : 1 } ) ,
) ;
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
2023-01-12 20:59:13 -05:00
}
}
2023-03-09 15:09:03 -05:00
}
]
} )
) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions_npm ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 " ,
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " npm:@denotest/cjs-default-export " , " npm:chalk " ] ,
" file:///a/file.ts " ,
] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
2023-01-12 20:59:13 -05:00
// check importing a cjs default import
2023-03-09 15:09:03 -05:00
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. "
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
client . read_diagnostics ( ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 2 , 11 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
} ) ,
) ;
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 " ) ) ;
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " MyClass " ,
" kind " : 6 ,
" sortText " : " 1 " ,
" insertTextFormat " : 1 ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 69 ,
" name " : " MyClass " ,
" useCodeSnippet " : false
2023-03-08 18:15:20 -05:00
}
2023-03-09 15:09:03 -05:00
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" label " : " MyClass " ,
" kind " : 6 ,
" sortText " : " 1 " ,
" insertTextFormat " : 1 ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 69 ,
" name " : " MyClass " ,
" useCodeSnippet " : false
}
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
// now check chalk, which is esm
2023-03-09 15:09:03 -05:00
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. "
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
client . read_diagnostics ( ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 2 , 6 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
} ) ,
) ;
assert! ( ! list . is_incomplete ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " green " ) ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " red " ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_npm_specifier_unopened_file ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-01-12 20:59:13 -05:00
// 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 ( ) )
2023-11-17 10:05:42 -05:00
. piped_output ( )
2023-01-12 20:59:13 -05:00
. 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 ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : main_url ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import { chalk } from './other.ts'; \n \n " ,
}
} ) ) ;
client . write_notification (
" textDocument/didChange " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
" uri " : main_url ,
2023-03-09 15:09:03 -05:00
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 2 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 0 }
} ,
" text " : " chalk. "
}
]
2023-01-12 20:59:13 -05:00
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . read_diagnostics ( ) ;
2023-01-12 20:59:13 -05:00
// now ensure completions work
2023-03-09 15:09:03 -05:00
let list = client . get_completion_list (
main_url ,
( 2 , 6 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
} ) ,
) ;
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 63 ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " ansi256 " ) ) ;
2023-01-12 20:59:13 -05:00
}
2023-01-24 09:05:54 -05:00
#[ test ]
2023-03-09 15:09:03 -05:00
fn lsp_completions_node_specifier ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-09 15:09:03 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import fs from 'node:non-existent'; \n \n " ,
}
} ) ) ;
2023-01-24 09:05:54 -05:00
let non_existent_diagnostics = diagnostics
2023-05-26 02:10:18 -04:00
. messages_with_file_and_source ( " file:///a/file.ts " , " deno " )
2023-01-24 09:05:54 -05:00
. diagnostics
. into_iter ( )
. filter ( | d | {
d . code = = Some ( lsp ::NumberOrString ::String ( " resolver-error " . to_string ( ) ) )
} )
. collect ::< Vec < _ > > ( ) ;
assert_eq! (
json! ( non_existent_diagnostics ) ,
json! ( [
{
" range " : {
2023-01-24 15:14:49 -05:00
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 34 } ,
2023-01-24 09:05:54 -05:00
} ,
" severity " : 1 ,
" code " : " resolver-error " ,
" source " : " deno " ,
" message " : " Unknown Node built-in module: non-existent "
}
] )
) ;
2023-01-24 15:14:49 -05:00
// update to have fs import
2023-03-09 15:09:03 -05:00
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 16 } ,
" end " : { " line " : 0 , " character " : 33 } ,
} ,
" text " : " fs "
}
]
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
2023-01-24 15:14:49 -05:00
let diagnostics = diagnostics
2023-05-26 02:10:18 -04:00
. messages_with_file_and_source ( " file:///a/file.ts " , " deno " )
2023-01-24 15:14:49 -05:00
. diagnostics
. into_iter ( )
. filter ( | d | {
d . code
= = Some ( lsp ::NumberOrString ::String (
2023-01-27 17:36:23 -05:00
" import-node-prefix-missing " . to_string ( ) ,
2023-01-24 15:14:49 -05:00
) )
} )
. collect ::< Vec < _ > > ( ) ;
// get the quick fixes
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 0 , " character " : 16 } ,
" end " : { " line " : 0 , " character " : 18 } ,
} ,
" context " : {
" diagnostics " : json ! ( diagnostics ) ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
2023-01-24 15:14:49 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-01-24 15:14:49 -05:00
" title " : " Update specifier to node:fs " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 19 }
} ,
" severity " : 1 ,
2023-01-27 17:36:23 -05:00
" code " : " import-node-prefix-missing " ,
2023-01-24 15:14:49 -05:00
" source " : " deno " ,
" message " : " Relative import path \" fs \" not prefixed with / or ./ or ../ \n If you want to use a built-in Node module, add a \" node: \" prefix (ex. \" node:fs \" ). " ,
" data " : {
" specifier " : " fs "
} ,
}
] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 19 }
2023-01-24 09:05:54 -05:00
} ,
2023-01-24 15:14:49 -05:00
" newText " : " \" node:fs \" "
}
]
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-24 15:14:49 -05:00
) ;
// update to have node:fs import
2023-03-09 15:09:03 -05:00
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 3 ,
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 19 } ,
} ,
" text " : " \" node:fs \" " ,
}
]
} ) ,
) ;
2023-01-24 15:14:49 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . read_diagnostics ( ) ;
2023-01-24 09:05:54 -05:00
let cache_diagnostics = diagnostics
2023-05-26 02:10:18 -04:00
. messages_with_file_and_source ( " file:///a/file.ts " , " deno " )
2023-01-24 09:05:54 -05:00
. diagnostics
. into_iter ( )
. filter ( | d | {
d . code = = Some ( lsp ::NumberOrString ::String ( " no-cache-npm " . to_string ( ) ) )
} )
. collect ::< Vec < _ > > ( ) ;
assert_eq! (
json! ( cache_diagnostics ) ,
json! ( [
{
" range " : {
2023-01-24 15:14:49 -05:00
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 24 }
2023-01-24 09:05:54 -05:00
} ,
" data " : {
" specifier " : " npm:@types/node " ,
} ,
" severity " : 1 ,
" code " : " no-cache-npm " ,
" source " : " deno " ,
2023-07-10 22:27:22 -04:00
" message " : " Uncached or missing npm package: @types/node "
2023-01-24 09:05:54 -05:00
}
] )
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ " npm:@types/node " ] , " file:///a/file.ts " ] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
2023-01-24 09:05:54 -05:00
2023-03-09 15:09:03 -05:00
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" version " : 4
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 2 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 0 }
} ,
" text " : " fs. "
2023-01-24 09:05:54 -05:00
}
2023-03-09 15:09:03 -05:00
]
} ) ,
) ;
client . read_diagnostics ( ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 2 , 3 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . "
} ) ,
) ;
assert! ( ! list . is_incomplete ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " writeFile " ) ) ;
assert! ( list . items . iter ( ) . any ( | i | i . label = = " writeFileSync " ) ) ;
2023-01-24 09:05:54 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-24 09:05:54 -05:00
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_completions_registry ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . add_test_server_suggestions ( ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ) ;
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 0 , 46 ) ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 3 ) ;
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " v2.0.0 " ,
" kind " : 19 ,
" detail " : " (version) " ,
" sortText " : " 0000000003 " ,
" filterText " : " http://localhost:4545/x/a@v2.0.0 " ,
" textEdit " : {
" range " : {
" start " : { " line " : 0 , " character " : 20 } ,
" end " : { " line " : 0 , " character " : 46 }
2023-01-12 20:59:13 -05:00
} ,
2023-03-09 15:09:03 -05:00
" newText " : " http://localhost:4545/x/a@v2.0.0 "
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" label " : " v2.0.0 " ,
" kind " : 19 ,
" detail " : " (version) " ,
" sortText " : " 0000000003 " ,
" filterText " : " http://localhost:4545/x/a@v2.0.0 " ,
" textEdit " : {
" range " : {
" start " : { " line " : 0 , " character " : 20 } ,
" end " : { " line " : 0 , " character " : 46 }
} ,
" newText " : " http://localhost:4545/x/a@v2.0.0 "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_completions_registry_empty ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . add_test_server_suggestions ( ) ;
} ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" \" "
}
} ) ) ;
let res = client . get_completion (
" file:///a/file.ts " ,
( 0 , 20 ) ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" triggerKind " : 2 ,
" triggerCharacter " : " \" "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
json! ( res ) ,
json! ( {
2023-03-08 18:15:20 -05:00
" isIncomplete " : false ,
" items " : [ {
" label " : " . " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " . " ,
" commitCharacters " : [ " \" " , " ' " ]
} , {
" label " : " .. " ,
" kind " : 19 ,
" detail " : " (local) " ,
" sortText " : " 1 " ,
" insertText " : " .. " ,
" commitCharacters " : [ " \" " , " ' " ]
} , {
" label " : " http://localhost:4545 " ,
" kind " : 19 ,
" detail " : " (registry) " ,
" sortText " : " 2 " ,
" textEdit " : {
" range " : {
" start " : { " line " : 0 , " character " : 20 } ,
" end " : { " line " : 0 , " character " : 20 }
} ,
" newText " : " http://localhost:4545 "
} ,
2023-10-13 09:38:34 -04:00
" commitCharacters " : [ " \" " , " ' " ]
2023-03-08 18:15:20 -05:00
} ]
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_auto_discover_registry ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ) ;
client . get_completion (
" file:///a/file.ts " ,
( 0 , 46 ) ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let ( method , res ) = client . read_notification ( ) ;
2023-01-12 20:59:13 -05:00
assert_eq! ( method , " deno/registryState " ) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
2023-01-12 20:59:13 -05:00
Some ( json! ( {
" origin " : " http://localhost:4545 " ,
" suggestions " : true ,
} ) )
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_cache_location ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_cache ( " .cache " ) . add_test_server_suggestions ( ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ) ;
2023-03-09 15:09:03 -05:00
let diagnostics =
client . did_open ( 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 // @deno-types= \" http://127.0.0.1:4545/type_definitions/foo.d.ts \" \n import * as b from \" http://127.0.0.1:4545/type_definitions/foo.js \" ; \n import * as c from \" http://127.0.0.1:4545/subdir/type_reference.js \" ; \n import * as d from \" http://127.0.0.1:4545/subdir/mod1.ts \" ; \n import * as e from \" data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo= \" ; \n import * as f from \" ./file_01.ts \" ; \n import * as g from \" http://localhost:4545/x/a/mod.ts \" ; \n \n console.log(a, b, c, d, e, f, g); \n "
}
} ) ) ;
2023-07-01 18:52:30 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 6 ) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 0 , " character " : 28 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 62 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 7 , " character " : 28 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 7 , " character " : 19 } ,
" end " : { " line " : 7 , " character " : 53 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
let cache_path = temp_dir . path ( ) . join ( " .cache " ) ;
assert! ( cache_path . is_dir ( ) ) ;
2023-08-01 20:49:09 -04:00
assert! ( ! cache_path . join ( " gen " ) . is_dir ( ) ) ; // not created because no emitting has occurred
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
/// 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 ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder
. set_suggest_imports_hosts ( vec! [
( " http://localhost:4545/ " . to_string ( ) , true ) ,
( " https://localhost:5545/ " . to_string ( ) , true ) ,
] )
. set_tls_certificate ( " " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
}
} ) ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
2023-03-08 18:15:20 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" https://localhost:5545/xTypeScriptTypes.js \" ; \n // @deno-types= \" https://localhost:5545/type_definitions/foo.d.ts \" \n import * as b from \" https://localhost:5545/type_definitions/foo.js \" ; \n import * as c from \" https://localhost:5545/subdir/type_reference.js \" ; \n import * as d from \" https://localhost:5545/subdir/mod1.ts \" ; \n import * as e from \" data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo= \" ; \n import * as f from \" ./file_01.ts \" ; \n import * as g from \" http://localhost:4545/x/a/mod.ts \" ; \n \n console.log(a, b, c, d, e, f, g); \n "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
let diagnostics = diagnostics . all ( ) ;
2023-07-01 18:52:30 -04:00
assert_eq! ( diagnostics . len ( ) , 6 ) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 0 , " character " : 28 }
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: https​://localhost:5545/xTypeScriptTypes.js \n "
} ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 63 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
} ,
" position " : { " line " : 7 , " character " : 28 }
} ) ,
2023-01-12 20:59:13 -05:00
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" 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 " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 7 , " character " : 19 } ,
" end " : { " line " : 7 , " character " : 53 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_diagnostics_warn_redirect ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 " ,
} ,
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " http://127.0.0.1:4545/x_deno_warning.js " ] ,
" file:///a/file.ts " ,
2023-03-09 15:09:03 -05:00
] ,
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-05-26 02:10:18 -04:00
diagnostics . messages_with_source ( " deno " ) ,
2023-01-12 20:59:13 -05:00
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 ) ,
}
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_redirect_quick_fix ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open (
2023-01-12 20:59:13 -05:00
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 " ,
} ,
} ) ,
) ;
2023-03-09 15:09:03 -05:00
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " http://127.0.0.1:4545/x_deno_warning.js " ] ,
" file:///a/file.ts " ,
2023-03-09 15:09:03 -05:00
] ,
} ) ,
) ;
2023-05-26 02:10:18 -04:00
let diagnostics = client
. read_diagnostics ( )
. messages_with_source ( " deno " )
. diagnostics ;
2023-03-09 15:09:03 -05:00
let res = 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 " ]
}
} ) ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" title " : " Update specifier to its redirected specifier. " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 60 }
} ,
" severity " : 3 ,
" code " : " redirect " ,
" source " : " deno " ,
" 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 \" . " ,
" data " : {
" specifier " : " http://127.0.0.1:4545/x_deno_warning.js " ,
" redirect " : " http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js "
}
}
] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 19 } ,
" end " : { " line " : 0 , " character " : 60 }
} ,
" newText " : " \" http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js \" "
}
]
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_diagnostics_deprecated ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /** @deprecated */ \n export const a = \" a \" ; \n \n a; \n " ,
} ,
} ) ) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-05-26 02:10:18 -04:00
json! ( diagnostics . all_messages ( ) ) ,
json! ( [ {
" 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. " ,
2023-09-24 03:18:51 -04:00
" relatedInformation " : [
{
" location " : {
" uri " : " file:///a/file.ts " ,
" range " : {
" start " : {
" line " : 0 ,
" character " : 4 ,
} ,
" end " : {
" line " : 0 ,
" character " : 16 ,
} ,
} ,
} ,
" message " : " The declaration was marked as deprecated here. " ,
} ,
] ,
2023-05-26 02:10:18 -04:00
" tags " : [ 2 ]
}
] ,
" version " : 1
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_diagnostics_deno_types ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client
. did_open ( json! ( {
2023-03-08 18:15:20 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " /// <reference types= \" https://example.com/a/b.d.ts \" /> \n /// <reference path= \" https://example.com/a/c.ts \" \n \n // @deno-types=https://example.com/a/d.d.ts \n import * as d from \" https://example.com/a/d.js \" ; \n \n // @deno-types= \" https://example.com/a/e.d.ts \" \n import * as e from \" https://example.com/a/e.js \" ; \n \n console.log(d, e); \n "
}
} ) ,
2023-03-09 15:09:03 -05:00
) ;
client . write_request (
" textDocument/documentSymbol " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
}
} ) ,
) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 5 ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_diagnostics_refresh_dependents ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file_00.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export const a = \" a \" ; \n " ,
} ,
} ) ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
" uri " : " file:///a/file_01.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export * from \" ./file_00.ts \" ; \n " ,
} ,
} ) ) ;
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
2023-01-12 20:59:13 -05:00
" 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! (
2023-05-26 02:10:18 -04:00
json! ( diagnostics
. messages_with_file_and_source ( " file:///a/file_02.ts " , " deno-ts " ) ) ,
2023-01-12 20:59:13 -05:00
json! ( {
" uri " : " file:///a/file_02.ts " ,
" diagnostics " : [
{
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 12 } ,
" end " : { " line " : 0 , " character " : 13 }
2023-01-12 20:59:13 -05:00
} ,
" 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
2023-03-09 15:09:03 -05:00
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 "
}
]
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 ) ; // no diagnostics now
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
assert_eq! ( client . queue_len ( ) , 0 ) ;
2023-01-12 20:59:13 -05:00
}
2023-10-17 00:13:06 -04:00
// Regression test for https://github.com/denoland/deno/issues/10897.
#[ test ]
fn lsp_ts_diagnostics_refresh_on_lsp_version_reset ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " file.ts " , r # "Deno.readTextFileSync(1);"# ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " file.ts " ) ,
} ,
} ) ) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 1 ) ;
client . write_notification (
" textDocument/didClose " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
} ,
} ) ,
) ;
temp_dir . remove_file ( " file.ts " ) ;
// VSCode opens with `version: 1` again because the file was deleted. Ensure
// diagnostics are still refreshed.
client . did_open_raw ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " " ,
} ,
} ) ) ;
temp_dir . write ( " file.ts " , r # ""# ) ;
client . did_save ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ,
} ,
} ) ) ;
let diagnostics = client . read_diagnostics ( ) ;
assert_eq! ( diagnostics . all ( ) , vec! [ ] ) ;
client . shutdown ( ) ;
}
2023-10-04 20:38:11 -04:00
#[ test ]
fn lsp_npm_missing_type_imports_diagnostics ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #"
import colorName , { type RGB } from ' npm :color - name ' ;
const color : RGB = colorName . black ;
console . log ( color ) ;
" #,
} ,
} ) ) ;
client . write_request (
" workspace/executeCommand " ,
json! ( {
" command " : " deno.cache " ,
" arguments " : [ [ ] , " file:///a/file.ts " ] ,
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
assert_eq! (
json! (
diagnostics . messages_with_file_and_source ( " file:///a/file.ts " , " deno-ts " )
) ,
json! ( {
" uri " : " file:///a/file.ts " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 1 , " character " : 33 } ,
" end " : { " line " : 1 , " character " : 36 } ,
} ,
" severity " : 1 ,
" code " : 2305 ,
" source " : " deno-ts " ,
" message " : " Module ' \" npm:color-name \" ' has no exported member 'RGB'. " ,
} ,
] ,
" version " : 1 ,
} )
) ;
}
2023-09-29 15:44:59 -04:00
#[ test ]
fn lsp_jupyter_diagnostics ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " deno-notebook-cell:/a/file.ts#abc " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " Deno.readTextFileSync(1234); " ,
} ,
} ) ) ;
assert_eq! (
json! ( diagnostics . all_messages ( ) ) ,
json! ( [
{
" uri " : " deno-notebook-cell:/a/file.ts#abc " ,
" diagnostics " : [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 22 ,
} ,
" end " : {
" line " : 0 ,
" character " : 26 ,
} ,
} ,
" severity " : 1 ,
" code " : 2345 ,
" source " : " deno-ts " ,
" message " : " Argument of type 'number' is not assignable to parameter of type 'string | URL'. " ,
} ,
] ,
" version " : 1 ,
} ,
] )
) ;
client . shutdown ( ) ;
}
2023-10-16 15:21:57 -04:00
#[ test ]
fn lsp_untitled_file_diagnostics ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : " untitled:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " Deno.readTextFileSync(1234); " ,
} ,
} ) ) ;
assert_eq! (
json! ( diagnostics . all_messages ( ) ) ,
json! ( [
{
" uri " : " untitled:///a/file.ts " ,
" diagnostics " : [
{
" range " : {
" start " : {
" line " : 0 ,
" character " : 22 ,
} ,
" end " : {
" line " : 0 ,
" character " : 26 ,
} ,
} ,
" severity " : 1 ,
" code " : 2345 ,
" source " : " deno-ts " ,
" message " : " Argument of type 'number' is not assignable to parameter of type 'string | URL'. " ,
} ,
] ,
" version " : 1 ,
} ,
] )
) ;
client . shutdown ( ) ;
}
2023-03-15 10:34:23 -04:00
#[ derive(Debug, Deserialize) ]
2023-01-12 20:59:13 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct PerformanceAverage {
pub name : String ,
pub count : u32 ,
pub average_duration : u32 ,
}
2023-03-15 10:34:23 -04:00
#[ derive(Debug, Deserialize) ]
2023-01-12 20:59:13 -05:00
#[ serde(rename_all = " camelCase " ) ]
struct PerformanceAverages {
averages : Vec < PerformanceAverage > ,
}
#[ test ]
fn lsp_performance ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console.log(Deno.args); \n "
}
} ) ) ;
client . write_request (
" textDocument/hover " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" position " : { " line " : 0 , " character " : 19 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
2023-03-09 15:09:03 -05:00
let res = client . write_request_with_res_as ::< PerformanceAverages > (
" deno/performance " ,
json! ( null ) ,
) ;
2023-03-15 10:34:23 -04:00
let mut averages = res
. averages
. iter ( )
. map ( | a | a . name . as_str ( ) )
. collect ::< Vec < _ > > ( ) ;
averages . sort ( ) ;
assert_eq! (
averages ,
vec! [
" did_open " ,
" hover " ,
" initialize " ,
" op_load " ,
" request " ,
" testing_update " ,
" update_cache " ,
" update_diagnostics_deps " ,
" update_diagnostics_lint " ,
" update_diagnostics_ts " ,
" update_import_map " ,
" update_registries " ,
" update_tsconfig " ,
]
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_no_changes ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console; \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
client . assert_no_notification ( " window/showMessage " ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_error ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " console test test \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert_eq! ( res , json! ( null ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_mbc ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " const bar = '👍🇺🇸😃' \n console.log('hello deno') \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-03-08 18:15:20 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 12 } ,
" end " : { " line " : 0 , " character " : 13 }
} ,
" newText " : " \" "
} , {
" range " : {
" start " : { " line " : 0 , " character " : 21 } ,
" end " : { " line " : 0 , " character " : 22 }
} ,
" newText " : " \" ; "
} , {
" range " : {
" start " : { " line " : 1 , " character " : 12 } ,
" end " : { " line " : 1 , " character " : 13 }
} ,
" newText " : " \" "
} , {
" range " : {
" start " : { " line " : 1 , " character " : 23 } ,
" end " : { " line " : 1 , " character " : 25 }
} ,
" newText " : " \" ); "
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_exclude_with_config ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.fmt.jsonc " ,
r #" {
" fmt " : {
" files " : {
" exclude " : [ " ignored.ts " ]
} ,
" options " : {
" useTabs " : true ,
" lineWidth " : 40 ,
" indentWidth " : 8 ,
" singleQuote " : true ,
" proseWrap " : " always "
}
}
} " #,
) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.fmt.jsonc " ) ;
} ) ;
let file_uri = temp_dir . uri ( ) . join ( " ignored.ts " ) . unwrap ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " function myFunc(){} "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : file_uri
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert_eq! ( res , json! ( null ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_exclude_default_config ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.fmt.jsonc " ,
r #" {
" fmt " : {
" files " : {
" exclude " : [ " ignored.ts " ]
} ,
" options " : {
" useTabs " : true ,
" lineWidth " : 40 ,
" indentWidth " : 8 ,
" singleQuote " : true ,
" proseWrap " : " always "
}
}
} " #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.fmt.jsonc " ) ;
} ) ;
let file_uri = temp_dir . uri ( ) . join ( " ignored.ts " ) . unwrap ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " function myFunc(){} "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : file_uri
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
2023-03-09 15:09:03 -05:00
assert_eq! ( res , json! ( null ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_json ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
// Also test out using a non-json file extension here.
// What should matter is the language identifier.
" uri " : " file:///a/file.lock " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" key \" : \" value \" } "
}
} ) ) ;
let res = client . write_request (
" textDocument/formatting " ,
json! ( {
2023-01-12 20:59:13 -05:00
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.lock "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [
2023-01-12 20:59:13 -05:00
{
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 1 } ,
" end " : { " line " : 0 , " character " : 1 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 7 }
} ,
" newText " : " "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 14 } ,
" end " : { " line " : 0 , " character " : 15 }
} ,
" newText " : " } \n "
}
2023-03-09 15:09:03 -05:00
] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_json_no_diagnostics ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.json " ,
" languageId " : " json " ,
" version " : 1 ,
" text " : " { \" key \" : \" value \" } "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json "
}
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.json "
} ,
" position " : { " line " : 0 , " character " : 3 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-09-19 11:37:27 -04:00
#[ test ]
fn lsp_json_import_with_query_string ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " data.json " , r # "{"k": "v"}"# ) ;
temp_dir . write (
" main.ts " ,
r #"
import data from " ./data.json?1 " with { type : " json " } ;
console . log ( data ) ;
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " data.json " ) . unwrap ( ) ,
" languageId " : " json " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " data.json " ) ,
}
} ) ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " main.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : temp_dir . read_to_string ( " main.ts " ) ,
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) , vec! [ ] ) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_format_markdown ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.md " ,
" languageId " : " markdown " ,
" version " : 1 ,
" text " : " # Hello World "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [
2023-01-12 20:59:13 -05:00
{
" range " : {
" start " : { " line " : 0 , " character " : 1 } ,
" end " : { " line " : 0 , " character " : 3 }
} ,
" newText " : " "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
" start " : { " line " : 0 , " character " : 15 } ,
" end " : { " line " : 0 , " character " : 15 }
} ,
" newText " : " \n "
}
2023-03-09 15:09:03 -05:00
] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_format_with_config ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.fmt.jsonc " ,
r #" {
" fmt " : {
" options " : {
" useTabs " : true ,
" lineWidth " : 40 ,
" indentWidth " : 8 ,
" singleQuote " : true ,
" proseWrap " : " always "
}
}
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
" #,
) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.fmt.jsonc " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
client
2023-03-09 15:09:03 -05:00
. did_open (
2023-01-12 20:59:13 -05:00
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 } "
}
} ) ,
2023-03-09 15:09:03 -05:00
) ;
2023-01-12 20:59:13 -05:00
// The options below should be ignored in favor of configuration from config file.
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/formatting " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" options " : {
" tabSize " : 2 ,
" insertSpaces " : true
}
} ) ,
) ;
2023-01-12 20:59:13 -05:00
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 1 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " \t "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 1 , " character " : 23 } ,
" end " : { " line " : 1 , " character " : 24 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " \n \t \t ' "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 1 , " character " : 73 } ,
" end " : { " line " : 1 , " character " : 74 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " ', \n \t "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 2 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 0 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " \t "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 3 , " character " : 0 } ,
" end " : { " line " : 3 , " character " : 0 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " \t "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 3 , " character " : 12 } ,
" end " : { " line " : 3 , " character " : 13 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " ' "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 3 , " character " : 22 } ,
" end " : { " line " : 3 , " character " : 24 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " '); "
2023-03-08 18:15:20 -05:00
} , {
2023-01-12 20:59:13 -05:00
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 4 , " character " : 1 } ,
" end " : { " line " : 4 , " character " : 1 }
2023-01-12 20:59:13 -05:00
} ,
" newText " : " \n "
} ]
2023-03-09 15:09:03 -05:00
)
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_markdown_no_diagnostics ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.md " ,
" languageId " : " markdown " ,
" version " : 1 ,
" text " : " # Hello World "
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/semanticTokens/full " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
}
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.md "
} ,
" position " : { " line " : 0 , " character " : 3 }
} ) ,
) ;
assert_eq! ( res , json! ( null ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_configuration_did_change ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import * as a from \" http://localhost:4545/x/a@ \" "
}
} ) ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( { " deno " : {
2023-03-15 10:34:23 -04:00
" enable " : true ,
" codeLens " : {
" implementations " : true ,
2023-10-24 16:27:27 -04:00
" references " : true ,
2023-03-15 10:34:23 -04:00
} ,
" importMap " : null ,
" lint " : true ,
" suggest " : {
" autoImports " : true ,
" completeFunctionCalls " : false ,
" names " : true ,
" paths " : true ,
" imports " : {
" hosts " : {
2023-10-24 16:27:27 -04:00
" http://localhost:4545/ " : true ,
} ,
} ,
2023-03-15 10:34:23 -04:00
} ,
2023-10-24 16:27:27 -04:00
" unstable " : false ,
} } ) ) ;
2023-03-15 10:34:23 -04:00
2023-03-09 15:09:03 -05:00
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 0 , 46 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " @ "
} ) ,
) ;
assert! ( ! list . is_incomplete ) ;
assert_eq! ( list . items . len ( ) , 3 ) ;
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " v2.0.0 " ,
" kind " : 19 ,
" detail " : " (version) " ,
" sortText " : " 0000000003 " ,
" filterText " : " http://localhost:4545/x/a@v2.0.0 " ,
" textEdit " : {
" range " : {
" start " : { " line " : 0 , " character " : 20 } ,
" end " : { " line " : 0 , " character " : 46 }
} ,
" newText " : " http://localhost:4545/x/a@v2.0.0 "
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-03-08 18:15:20 -05:00
" label " : " v2.0.0 " ,
" kind " : 19 ,
" detail " : " (version) " ,
" sortText " : " 0000000003 " ,
" filterText " : " http://localhost:4545/x/a@v2.0.0 " ,
" textEdit " : {
" range " : {
" start " : { " line " : 0 , " character " : 20 } ,
" end " : { " line " : 0 , " character " : 46 }
} ,
" newText " : " http://localhost:4545/x/a@v2.0.0 "
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-08-25 20:53:44 -04:00
#[ test ]
fn lsp_completions_complete_function_calls ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " []. "
}
} ) ) ;
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( {
2023-09-21 01:46:39 -04:00
" deno " : {
" enable " : true ,
} ,
" typescript " : {
" suggest " : {
" completeFunctionCalls " : true ,
} ,
2023-08-25 20:53:44 -04:00
} ,
2023-10-24 16:27:27 -04:00
} ) ) ;
2023-08-25 20:53:44 -04:00
let list = client . get_completion_list (
" file:///a/file.ts " ,
( 0 , 3 ) ,
json! ( {
" triggerKind " : 2 ,
" triggerCharacter " : " . " ,
} ) ,
) ;
assert! ( ! list . is_incomplete ) ;
let res = client . write_request (
" completionItem/resolve " ,
json! ( {
" label " : " map " ,
" kind " : 2 ,
" sortText " : " 1 " ,
" insertTextFormat " : 1 ,
" data " : {
" tsc " : {
" specifier " : " file:///a/file.ts " ,
" position " : 3 ,
" name " : " map " ,
" useCodeSnippet " : true
}
}
} ) ,
) ;
assert_eq! (
res ,
json! ( {
" label " : " map " ,
" kind " : 2 ,
" detail " : " (method) Array<never>.map<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any): U[] " ,
" documentation " : {
" kind " : " markdown " ,
" value " : " Calls a defined callback function on each element of an array, and returns an array that contains the results. \n \n *@param* - callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.*@param* - thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. "
} ,
" sortText " : " 1 " ,
" insertText " : " map(callbackfn) " ,
" insertTextFormat " : 1
} )
) ;
client . shutdown ( ) ;
}
2023-01-12 20:59:13 -05:00
#[ test ]
fn lsp_workspace_symbol ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " export class A { \n fieldA: string; \n fieldB: string; \n } \n " ,
}
} ) ) ;
client . did_open ( 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 res = client . write_request (
" workspace/symbol " ,
2023-01-12 20:59:13 -05:00
json! ( {
2023-03-09 15:09:03 -05:00
" query " : " field "
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
2023-03-21 11:46:40 -04:00
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 "
} , {
" name " : " ClassFieldDecoratorContext " ,
" kind " : 11 ,
" location " : {
" uri " : " deno:/asset/lib.decorators.d.ts " ,
" range " : {
" start " : {
2023-09-09 15:03:21 -04:00
" line " : 346 ,
2023-03-21 11:46:40 -04:00
" character " : 0 ,
} ,
" end " : {
2023-09-09 15:03:21 -04:00
" line " : 388 ,
2023-03-21 11:46:40 -04:00
" character " : 1 ,
} ,
2023-01-12 20:59:13 -05:00
} ,
2023-03-21 11:46:40 -04:00
} ,
" containerName " : " " ,
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_code_actions_ignore_lint ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " let message = 'Hello, Deno!'; \n console.log(message); \n "
}
} ) ) ;
let res = client . write_request (
" textDocument/codeAction " ,
2023-01-12 20:59:13 -05:00
json! ( {
" textDocument " : {
2023-03-09 15:09:03 -05:00
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 1 , " character " : 5 } ,
" end " : { " line " : 1 , " character " : 12 }
} ,
" context " : {
" diagnostics " : [
{
" range " : {
" start " : { " line " : 1 , " character " : 5 } ,
" end " : { " line " : 1 , " character " : 12 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'message' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
}
] ,
" only " : [ " quickfix " ]
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for this line " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 5 } ,
" end " : { " line " : 1 , " character " : 12 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'message' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
2023-09-18 12:39:28 -04:00
" start " : { " line " : 1 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 0 }
2023-03-08 18:15:20 -05:00
} ,
2023-09-18 12:39:28 -04:00
" newText " : " // deno-lint-ignore prefer-const \n "
2023-03-08 18:15:20 -05:00
} ]
}
}
} , {
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for the entire file " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 5 } ,
" end " : { " line " : 1 , " character " : 12 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'message' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
2023-09-18 12:39:28 -04:00
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
2023-03-08 18:15:20 -05:00
} ,
2023-09-18 12:39:28 -04:00
" newText " : " // deno-lint-ignore-file prefer-const \n "
2023-03-08 18:15:20 -05:00
} ]
}
}
} , {
" title " : " Ignore lint errors for the entire file " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 1 , " character " : 5 } ,
" end " : { " line " : 1 , " character " : 12 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'message' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " // deno-lint-ignore-file \n "
} ]
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
/// This test exercises updating an existing deno-lint-ignore-file comment.
#[ test ]
fn lsp_code_actions_update_ignore_lint ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
2023-03-08 18:15:20 -05:00
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " :
2023-01-12 20:59:13 -05:00
" #!/usr/bin/env -S deno run
// deno-lint-ignore-file camelcase
let snake_case = ' Hello , Deno ! ' ;
console . log ( snake_case ) ;
" ,
2023-03-09 15:09:03 -05:00
}
} ) ) ;
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.ts "
} ,
" range " : {
" start " : { " line " : 3 , " character " : 5 } ,
" end " : { " line " : 3 , " character " : 15 }
} ,
" context " : {
" diagnostics " : [ {
" range " : {
" start " : { " line " : 3 , " character " : 5 } ,
" end " : { " line " : 3 , " character " : 15 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'snake_case' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" only " : [ " quickfix " ]
2023-01-12 20:59:13 -05:00
}
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( [ {
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for this line " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 3 , " character " : 5 } ,
" end " : { " line " : 3 , " character " : 15 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'snake_case' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
2023-09-18 12:39:28 -04:00
" start " : { " line " : 3 , " character " : 0 } ,
" end " : { " line " : 3 , " character " : 0 }
2023-03-08 18:15:20 -05:00
} ,
2023-09-18 12:39:28 -04:00
" newText " : " // deno-lint-ignore prefer-const \n "
2023-03-08 18:15:20 -05:00
} ]
}
}
} , {
2023-09-18 12:39:28 -04:00
" title " : " Disable prefer-const for the entire file " ,
2023-03-08 18:15:20 -05:00
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 3 , " character " : 5 } ,
" end " : { " line " : 3 , " character " : 15 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'snake_case' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
2023-09-18 12:39:28 -04:00
" start " : { " line " : 1 , " character " : 34 } ,
" end " : { " line " : 1 , " character " : 34 }
2023-03-08 18:15:20 -05:00
} ,
2023-09-18 12:39:28 -04:00
" newText " : " prefer-const "
2023-03-08 18:15:20 -05:00
} ]
}
}
} , {
" title " : " Ignore lint errors for the entire file " ,
" kind " : " quickfix " ,
" diagnostics " : [ {
" range " : {
" start " : { " line " : 3 , " character " : 5 } ,
" end " : { " line " : 3 , " character " : 15 }
} ,
" severity " : 1 ,
" code " : " prefer-const " ,
" source " : " deno-lint " ,
" message " : " 'snake_case' is never reassigned \n Use 'const' instead " ,
" relatedInformation " : [ ]
} ] ,
" edit " : {
" changes " : {
" file:///a/file.ts " : [ {
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 0 , " character " : 0 }
} ,
" newText " : " // deno-lint-ignore-file \n "
} ]
}
}
2023-03-09 15:09:03 -05:00
} ] )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_lint_with_config ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.lint.jsonc " ,
r #" {
" lint " : {
" rules " : {
" exclude " : [ " camelcase " ] ,
" include " : [ " ban-untagged-todo " ] ,
" tags " : [ ]
}
}
2023-01-12 20:59:13 -05:00
}
2023-03-08 18:15:20 -05:00
" #,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.lint.jsonc " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open ( json! ( {
2023-03-08 18:15:20 -05:00
" textDocument " : {
" uri " : " file:///a/file.ts " ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " // TODO: fixme \n export async function non_camel_case() { \n console.log( \" finished! \" ) \n } "
}
} ) ) ;
2023-05-26 02:10:18 -04:00
let diagnostics = diagnostics . all ( ) ;
2023-01-12 20:59:13 -05:00
assert_eq! ( diagnostics . len ( ) , 1 ) ;
assert_eq! (
diagnostics [ 0 ] . code ,
Some ( lsp ::NumberOrString ::String ( " ban-untagged-todo " . to_string ( ) ) )
) ;
2023-03-09 15:09:03 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_lint_exclude_with_config ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
temp_dir . write (
" deno.lint.jsonc " ,
r #" {
" lint " : {
" files " : {
" exclude " : [ " ignored.ts " ]
} ,
" rules " : {
" exclude " : [ " camelcase " ] ,
" include " : [ " ban-untagged-todo " ] ,
" tags " : [ ]
}
}
} " #,
) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_config ( " ./deno.lint.jsonc " ) ;
} ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let diagnostics = client . did_open (
2023-01-12 20:59:13 -05:00
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 } "
}
} ) ,
) ;
2023-05-26 02:10:18 -04:00
let diagnostics = diagnostics . all ( ) ;
2023-01-12 20:59:13 -05:00
assert_eq! ( diagnostics , Vec ::new ( ) ) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ test ]
fn lsp_jsx_import_source_pragma ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : " file:///a/file.tsx " ,
" languageId " : " typescriptreact " ,
" version " : 1 ,
" text " :
2023-01-12 20:59:13 -05:00
" /** @jsxImportSource http://localhost:4545/jsx */
function A ( ) {
return \ " hello \" ;
}
export function B ( ) {
return < A > < / A > ;
}
" ,
2023-03-09 15:09:03 -05:00
}
} ) ) ;
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-03-09 15:09:03 -05:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [
[ " http://127.0.0.1:4545/jsx/jsx-runtime " ] ,
" file:///a/file.tsx " ,
] ,
2023-03-09 15:09:03 -05:00
} ) ,
) ;
let res = client . write_request (
" textDocument/hover " ,
json! ( {
" textDocument " : {
" uri " : " file:///a/file.tsx "
} ,
" position " : { " line " : 0 , " character " : 25 }
2023-01-12 20:59:13 -05:00
} ) ,
) ;
assert_eq! (
2023-03-09 15:09:03 -05:00
res ,
json! ( {
2023-01-12 20:59:13 -05:00
" contents " : {
" kind " : " markdown " ,
" value " : " **Resolved Dependency** \n \n **Code**: http​://localhost:4545/jsx/jsx-runtime \n " ,
} ,
" range " : {
2023-03-08 18:15:20 -05:00
" start " : { " line " : 0 , " character " : 21 } ,
" end " : { " line " : 0 , " character " : 46 }
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
#[ 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 ( ) {
2023-03-30 17:47:53 -04:00
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
2023-03-15 10:34:23 -04:00
let temp_dir = context . temp_dir ( ) ;
2023-01-12 20:59:13 -05:00
let contents = r #"
Deno . test ( {
name : " test a " ,
2023-08-25 17:32:22 -04:00
async fn ( t ) {
2023-01-12 20:59:13 -05:00
console . log ( " test a " ) ;
2023-08-25 17:32:22 -04:00
await t . step ( " step of test a " , ( ) = > { } ) ;
2023-01-12 20:59:13 -05:00
}
} ) ;
" #;
2023-03-08 18:15:20 -05:00
temp_dir . write ( " ./test.ts " , contents ) ;
temp_dir . write ( " ./deno.jsonc " , " {} " ) ;
let specifier = temp_dir . uri ( ) . join ( " test.ts " ) . unwrap ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-08 18:15:20 -05:00
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
client . did_open ( json! ( {
" textDocument " : {
" uri " : specifier ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : contents ,
}
} ) ) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let notification =
client . read_notification_with_method ::< Value > ( " 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_eq! (
test . range ,
Some ( lsp ::Range {
start : lsp ::Position {
line : 1 ,
character : 5 ,
} ,
end : lsp ::Position {
line : 1 ,
character : 9 ,
2023-01-12 20:59:13 -05:00
}
2023-03-09 15:09:03 -05:00
} )
2023-01-12 20:59:13 -05:00
) ;
2023-08-25 17:32:22 -04:00
let steps = test . steps . as_ref ( ) . unwrap ( ) ;
assert_eq! ( steps . len ( ) , 1 ) ;
let step = & steps [ 0 ] ;
assert_eq! ( step . label , " step of test a " ) ;
assert_eq! (
step . range ,
Some ( lsp ::Range {
start : lsp ::Position {
line : 5 ,
character : 12 ,
} ,
end : lsp ::Position {
line : 5 ,
character : 16 ,
}
} )
) ;
2023-01-12 20:59:13 -05:00
2023-03-09 15:09:03 -05:00
let res = client . write_request_with_res_as ::< TestRunResponseParams > (
" deno/testRun " ,
json! ( {
" id " : 1 ,
" kind " : " run " ,
} ) ,
) ;
2023-01-12 20:59:13 -05:00
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 ( ) ;
2023-03-09 15:09:03 -05:00
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
2023-01-12 20:59:13 -05:00
assert_eq! ( method , " deno/testRunProgress " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" id " : 1 ,
" message " : {
" type " : " started " ,
" test " : {
" textDocument " : {
" uri " : specifier ,
} ,
" id " : id ,
} ,
}
} ) )
) ;
2023-03-09 15:09:03 -05:00
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
2023-01-12 20:59:13 -05:00
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
2023-06-26 09:10:27 -04:00
// synchronize the output pipes. Occasionally this zero width space
2023-01-12 20:59:13 -05:00
// 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 ,
} ,
}
} ) )
) ;
2023-03-09 15:09:03 -05:00
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
2023-08-25 17:32:22 -04:00
assert_eq! ( method , " deno/testRunProgress " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" id " : 1 ,
" message " : {
" type " : " started " ,
" test " : {
" textDocument " : {
" uri " : specifier ,
} ,
" id " : id ,
" stepId " : step . id ,
} ,
}
} ) )
) ;
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
assert_eq! ( method , " deno/testRunProgress " ) ;
let mut notification = notification . unwrap ( ) ;
let duration = notification
. as_object_mut ( )
. unwrap ( )
. get_mut ( " message " )
. unwrap ( )
. as_object_mut ( )
. unwrap ( )
. remove ( " duration " ) ;
assert! ( duration . is_some ( ) ) ;
assert_eq! (
notification ,
json! ( {
" id " : 1 ,
" message " : {
" type " : " passed " ,
" test " : {
" textDocument " : {
" uri " : specifier ,
} ,
" id " : id ,
" stepId " : step . id ,
} ,
}
} )
) ;
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
2023-01-12 20:59:13 -05:00
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 " ) ) ;
2023-03-09 15:09:03 -05:00
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
2023-01-12 20:59:13 -05:00
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 ) ) ,
}
2023-08-29 04:43:11 -04:00
// Regression test for https://github.com/denoland/vscode_deno/issues/899.
temp_dir . write ( " ./test.ts " , " " ) ;
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " test.ts " ) . unwrap ( ) ,
" version " : 2
} ,
" contentChanges " : [ { " text " : " " } ] ,
} ) ,
) ;
assert_eq! ( client . read_diagnostics ( ) . all ( ) . len ( ) , 0 ) ;
let ( method , notification ) = client . read_notification ::< Value > ( ) ;
assert_eq! ( method , " deno/testModuleDelete " ) ;
assert_eq! (
notification ,
Some ( json! ( {
" textDocument " : {
" uri " : temp_dir . uri ( ) . join ( " test.ts " ) . unwrap ( )
}
} ) )
) ;
2023-03-08 18:15:20 -05:00
client . shutdown ( ) ;
2023-01-12 20:59:13 -05:00
}
2023-03-30 17:47:53 -04:00
#[ test ]
fn lsp_closed_file_find_references ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write ( " ./mod.ts " , " export const a = 5; " ) ;
temp_dir . write (
" ./mod.test.ts " ,
" import { a } from './mod.ts'; console.log(a); " ,
) ;
let temp_dir_url = temp_dir . uri ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " mod.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export const a = 5; " #
}
} ) ) ;
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " mod.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 13 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : temp_dir_url . join ( " mod.test.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 0 , " character " : 9 } ,
" end " : { " line " : 0 , " character " : 10 }
}
} , {
" uri " : temp_dir_url . join ( " mod.test.ts " ) . unwrap ( ) ,
" range " : {
" start " : { " line " : 0 , " character " : 42 } ,
" end " : { " line " : 0 , " character " : 43 }
}
} ] )
) ;
client . shutdown ( ) ;
}
2023-03-31 12:48:18 -04:00
2023-05-11 17:17:14 -04:00
#[ test ]
fn lsp_closed_file_find_references_low_document_pre_load ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . create_dir_all ( " sub_dir " ) ;
temp_dir . write ( " ./other_file.ts " , " export const b = 5; " ) ;
temp_dir . write ( " ./sub_dir/mod.ts " , " export const a = 5; " ) ;
temp_dir . write (
" ./sub_dir/mod.test.ts " ,
" import { a } from './mod.ts'; console.log(a); " ,
) ;
let temp_dir_url = temp_dir . uri ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize ( | builder | {
builder . set_preload_limit ( 1 ) ;
} ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " sub_dir/mod.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export const a = 5; " #
}
} ) ) ;
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " sub_dir/mod.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 13 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
// won't have results because the document won't be pre-loaded
assert_eq! ( res , json! ( [ ] ) ) ;
client . shutdown ( ) ;
}
2023-06-13 15:48:53 -04:00
#[ test ]
fn lsp_closed_file_find_references_excluded_path ( ) {
// we exclude any files or folders in the "exclude" part of
// the config file from being pre-loaded
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . create_dir_all ( " sub_dir " ) ;
temp_dir . create_dir_all ( " other_dir/sub_dir " ) ;
temp_dir . write ( " ./sub_dir/mod.ts " , " export const a = 5; " ) ;
temp_dir . write (
" ./sub_dir/mod.test.ts " ,
" import { a } from './mod.ts'; console.log(a); " ,
) ;
temp_dir . write (
" ./other_dir/sub_dir/mod.test.ts " ,
" import { a } from '../../sub_dir/mod.ts'; console.log(a); " ,
) ;
temp_dir . write (
" deno.json " ,
r #" {
" exclude " : [
" ./sub_dir/mod.test.ts " ,
" ./other_dir/sub_dir " ,
]
} " #,
) ;
let temp_dir_url = temp_dir . uri ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " sub_dir/mod.ts " ) . unwrap ( ) ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : r #" export const a = 5; " #
}
} ) ) ;
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : temp_dir_url . join ( " sub_dir/mod.ts " ) . unwrap ( ) ,
} ,
" position " : { " line " : 0 , " character " : 13 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
// won't have results because the documents won't be pre-loaded
assert_eq! ( res , json! ( [ ] ) ) ;
client . shutdown ( ) ;
}
2023-03-31 12:48:18 -04:00
#[ test ]
fn lsp_data_urls_with_jsx_compiler_option ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
temp_dir . write (
" deno.json " ,
r # "{ "compilerOptions": { "jsx": "react-jsx" } }"# ,
) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let uri = Url ::from_file_path ( temp_dir . path ( ) . join ( " main.ts " ) ) . unwrap ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import a from \" data:application/typescript,export default 5; \" ; \n a; "
}
2023-05-26 02:10:18 -04:00
} ) ) . all ( ) ;
2023-03-31 12:48:18 -04:00
2023-07-01 18:52:30 -04:00
assert_eq! ( diagnostics . len ( ) , 0 ) ;
2023-03-31 12:48:18 -04:00
2023-07-01 18:52:30 -04:00
let res : Value = client . write_request (
2023-03-31 12:48:18 -04:00
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : uri
} ,
" position " : { " line " : 1 , " character " : 1 } ,
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" uri " : uri ,
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 8 }
}
} , {
" uri " : uri ,
" range " : {
" start " : { " line " : 1 , " character " : 0 } ,
" end " : { " line " : 1 , " character " : 1 }
}
2023-04-03 08:05:39 -04:00
} , {
2023-09-29 15:44:59 -04:00
" uri " : " deno:/ed0224c51f7e2a845dfc0941ed6959675e5e3e3d2a39b127f0ff569c1ffda8d8/data_url.ts " ,
2023-04-03 08:05:39 -04:00
" range " : {
" start " : { " line " : 0 , " character " : 7 } ,
" end " : { " line " : 0 , " character " : 14 } ,
} ,
2023-03-31 12:48:18 -04:00
} ] )
) ;
client . shutdown ( ) ;
}
2023-05-22 21:28:36 -04:00
#[ test ]
fn lsp_node_modules_dir ( ) {
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
// having a package.json should have no effect on whether
// a node_modules dir is created
temp_dir . write ( " package.json " , " {} " ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
let file_uri = temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ;
client . did_open ( json! ( {
" textDocument " : {
" uri " : file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : " import chalk from 'npm:chalk'; \n import path from 'node:path'; \n \n console.log(chalk.green(path.join('a', 'b'))); " ,
}
} ) ) ;
let cache = | client : & mut LspClient | {
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-05-22 21:28:36 -04:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ " npm:chalk " , " npm:@types/node " ] , file_uri ] ,
2023-05-22 21:28:36 -04:00
} ) ,
) ;
} ;
cache ( & mut client ) ;
assert! ( ! temp_dir . path ( ) . join ( " node_modules " ) . exists ( ) ) ;
temp_dir . write (
temp_dir . path ( ) . join ( " deno.json " ) ,
" { \" nodeModulesDir \" : true, \" lock \" : false } \n " ,
) ;
let refresh_config = | client : & mut LspClient | {
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( { " deno " : {
2023-05-22 21:28:36 -04:00
" enable " : true ,
" config " : " ./deno.json " ,
" codeLens " : {
" implementations " : true ,
2023-10-24 16:27:27 -04:00
" references " : true ,
2023-05-22 21:28:36 -04:00
} ,
" importMap " : null ,
" lint " : false ,
" suggest " : {
" autoImports " : true ,
" completeFunctionCalls " : false ,
" names " : true ,
" paths " : true ,
2023-10-24 16:27:27 -04:00
" imports " : { } ,
} ,
" unstable " : false ,
} } ) ) ;
2023-05-22 21:28:36 -04:00
} ;
refresh_config ( & mut client ) ;
let diagnostics = client . read_diagnostics ( ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 2 , " {:#?} " , diagnostics ) ; // not cached
2023-05-22 21:28:36 -04:00
cache ( & mut client ) ;
assert! ( temp_dir . path ( ) . join ( " node_modules/chalk " ) . exists ( ) ) ;
assert! ( temp_dir . path ( ) . join ( " node_modules/@types/node " ) . exists ( ) ) ;
assert! ( ! temp_dir . path ( ) . join ( " deno.lock " ) . exists ( ) ) ;
// now add a lockfile and cache
temp_dir . write (
temp_dir . path ( ) . join ( " deno.json " ) ,
" { \" nodeModulesDir \" : true } \n " ,
) ;
refresh_config ( & mut client ) ;
cache ( & mut client ) ;
let diagnostics = client . read_diagnostics ( ) ;
2023-05-26 02:10:18 -04:00
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 , " {:#?} " , diagnostics ) ;
2023-05-22 21:28:36 -04:00
assert! ( temp_dir . path ( ) . join ( " deno.lock " ) . exists ( ) ) ;
// the declaration should be found in the node_modules directory
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : file_uri ,
} ,
" position " : { " line " : 0 , " character " : 7 } , // chalk
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
// ensure that it's using the node_modules directory
let references = res . as_array ( ) . unwrap ( ) ;
assert_eq! ( references . len ( ) , 2 , " references: {:#?} " , references ) ;
let uri = references [ 1 ]
. as_object ( )
. unwrap ( )
. get ( " uri " )
. unwrap ( )
. as_str ( )
. unwrap ( ) ;
// canonicalize for mac
2023-06-10 11:09:45 -04:00
let path = temp_dir . path ( ) . join ( " node_modules " ) . canonicalize ( ) ;
2023-05-22 21:28:36 -04:00
assert_starts_with! (
uri ,
ModuleSpecifier ::from_file_path ( & path ) . unwrap ( ) . as_str ( )
) ;
client . shutdown ( ) ;
}
2023-08-01 20:49:09 -04:00
#[ test ]
2023-08-06 21:56:56 -04:00
fn lsp_vendor_dir ( ) {
2023-08-01 20:49:09 -04:00
let context = TestContextBuilder ::new ( )
. use_http_server ( )
. use_temp_cwd ( )
. build ( ) ;
let temp_dir = context . temp_dir ( ) ;
let mut client = context . new_lsp_command ( ) . build ( ) ;
client . initialize_default ( ) ;
2023-08-02 16:57:25 -04:00
let local_file_uri = temp_dir . uri ( ) . join ( " file.ts " ) . unwrap ( ) ;
2023-08-01 20:49:09 -04:00
client . did_open ( json! ( {
" textDocument " : {
2023-08-02 16:57:25 -04:00
" uri " : local_file_uri ,
2023-08-01 20:49:09 -04:00
" languageId " : " typescript " ,
" version " : 1 ,
2023-08-02 16:57:25 -04:00
" text " : " import { returnsHi } from 'http://localhost:4545/subdir/mod1.ts'; \n const test: string = returnsHi(); \n console.log(test); " ,
2023-08-01 20:49:09 -04:00
}
} ) ) ;
let cache = | client : & mut LspClient | {
client . write_request (
2023-09-26 16:57:14 -04:00
" workspace/executeCommand " ,
2023-08-01 20:49:09 -04:00
json! ( {
2023-09-26 16:57:14 -04:00
" command " : " deno.cache " ,
" arguments " : [ [ " http://localhost:4545/subdir/mod1.ts " ] , local_file_uri ] ,
2023-08-01 20:49:09 -04:00
} ) ,
) ;
} ;
cache ( & mut client ) ;
2023-08-06 21:56:56 -04:00
assert! ( ! temp_dir . path ( ) . join ( " vendor " ) . exists ( ) ) ;
2023-08-01 20:49:09 -04:00
temp_dir . write (
temp_dir . path ( ) . join ( " deno.json " ) ,
2023-08-06 21:56:56 -04:00
" { \" vendor \" : true, \" lock \" : false } \n " ,
2023-08-01 20:49:09 -04:00
) ;
let refresh_config = | client : & mut LspClient | {
2023-10-24 16:27:27 -04:00
client . change_configuration ( json! ( { " deno " : {
2023-08-01 20:49:09 -04:00
" enable " : true ,
" config " : " ./deno.json " ,
" codeLens " : {
" implementations " : true ,
2023-10-24 16:27:27 -04:00
" references " : true ,
2023-08-01 20:49:09 -04:00
} ,
" importMap " : null ,
" lint " : false ,
" suggest " : {
" autoImports " : true ,
" completeFunctionCalls " : false ,
" names " : true ,
" paths " : true ,
2023-10-24 16:27:27 -04:00
" imports " : { } ,
} ,
" unstable " : false ,
} } ) ) ;
2023-08-01 20:49:09 -04:00
} ;
refresh_config ( & mut client ) ;
let diagnostics = client . read_diagnostics ( ) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 0 , " {:#?} " , diagnostics ) ; // cached
// no caching necessary because it was already cached. It should exist now
assert! ( temp_dir
. path ( )
2023-08-06 21:56:56 -04:00
. join ( " vendor/http_localhost_4545/subdir/mod1.ts " )
2023-08-01 20:49:09 -04:00
. exists ( ) ) ;
2023-08-06 21:56:56 -04:00
// the declaration should be found in the vendor directory
2023-08-02 16:57:25 -04:00
let res = client . write_request (
" textDocument/references " ,
json! ( {
" textDocument " : {
" uri " : local_file_uri ,
} ,
" position " : { " line " : 0 , " character " : 9 } , // returnsHi
" context " : {
" includeDeclaration " : false
}
} ) ,
) ;
2023-08-06 21:56:56 -04:00
// ensure that it's using the vendor directory
2023-08-02 16:57:25 -04:00
let references = res . as_array ( ) . unwrap ( ) ;
assert_eq! ( references . len ( ) , 2 , " references: {:#?} " , references ) ;
let uri = references [ 1 ]
. as_object ( )
. unwrap ( )
. get ( " uri " )
. unwrap ( )
. as_str ( )
. unwrap ( ) ;
let file_path = temp_dir
. path ( )
2023-08-06 21:56:56 -04:00
. join ( " vendor/http_localhost_4545/subdir/mod1.ts " ) ;
2023-08-02 16:57:25 -04:00
let remote_file_uri = file_path . uri_file ( ) ;
assert_eq! ( uri , remote_file_uri . as_str ( ) ) ;
let file_text = file_path . read_to_string ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : remote_file_uri ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : file_text ,
}
} ) ) ;
assert_eq! ( diagnostics . all ( ) , Vec ::new ( ) ) ;
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : remote_file_uri ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 17 , " character " : 0 } ,
} ,
" text " : " export function returnsHi(): number { return new Date(); } "
}
]
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
assert_eq! (
json! (
diagnostics
. messages_with_file_and_source ( remote_file_uri . as_str ( ) , " deno-ts " )
. diagnostics
) ,
json! ( [
{
" range " : {
" start " : { " line " : 0 , " character " : 38 } ,
" end " : { " line " : 0 , " character " : 44 }
} ,
" severity " : 1 ,
" code " : 2322 ,
" source " : " deno-ts " ,
" message " : " Type 'Date' is not assignable to type 'number'. "
}
] ) ,
) ;
assert_eq! (
json! (
diagnostics
. messages_with_file_and_source ( local_file_uri . as_str ( ) , " deno-ts " )
. diagnostics
) ,
json! ( [
{
" range " : {
" start " : { " line " : 1 , " character " : 6 } ,
" end " : { " line " : 1 , " character " : 10 }
} ,
" severity " : 1 ,
" code " : 2322 ,
" source " : " deno-ts " ,
" message " : " Type 'number' is not assignable to type 'string'. "
}
] ) ,
) ;
assert_eq! ( diagnostics . all ( ) . len ( ) , 2 ) ;
2023-08-17 12:14:22 -04:00
// now try doing a relative import into the vendor directory
client . write_notification (
" textDocument/didChange " ,
json! ( {
" textDocument " : {
" uri " : local_file_uri ,
" version " : 2
} ,
" contentChanges " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 0 } ,
" end " : { " line " : 2 , " character " : 0 } ,
} ,
" text " : " import { returnsHi } from './vendor/subdir/mod1.ts'; \n const test: string = returnsHi(); \n console.log(test); "
}
]
} ) ,
) ;
let diagnostics = client . read_diagnostics ( ) ;
assert_eq! (
json! (
diagnostics
. messages_with_file_and_source ( local_file_uri . as_str ( ) , " deno " )
. diagnostics
) ,
json! ( [
{
" range " : {
" start " : { " line " : 0 , " character " : 26 } ,
" end " : { " line " : 0 , " character " : 51 }
} ,
" severity " : 1 ,
" code " : " resolver-error " ,
" source " : " deno " ,
" message " : " Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring. "
}
] ) ,
) ;
2023-08-01 20:49:09 -04:00
client . shutdown ( ) ;
}
2023-10-20 00:02:08 -04:00
#[ test ]
fn lsp_import_unstable_bare_node_builtins_auto_discovered ( ) {
let context = TestContextBuilder ::new ( ) . use_temp_cwd ( ) . build ( ) ;
let temp_dir = context . temp_dir ( ) ;
let contents = r # "import path from "path";"# ;
temp_dir . write ( " main.ts " , contents ) ;
temp_dir . write ( " deno.json " , r # "{ "unstable": ["bare-node-builtins"] }"# ) ;
let main_script = temp_dir . uri ( ) . join ( " main.ts " ) . unwrap ( ) ;
let mut client = context . new_lsp_command ( ) . capture_stderr ( ) . build ( ) ;
client . initialize_default ( ) ;
let diagnostics = client . did_open ( json! ( {
" textDocument " : {
" uri " : main_script ,
" languageId " : " typescript " ,
" version " : 1 ,
" text " : contents ,
}
} ) ) ;
let diagnostics = diagnostics
. messages_with_file_and_source ( main_script . as_ref ( ) , " deno " )
. diagnostics
. into_iter ( )
. filter ( | d | {
d . code
= = Some ( lsp ::NumberOrString ::String (
" import-node-prefix-missing " . to_string ( ) ,
) )
} )
. collect ::< Vec < _ > > ( ) ;
// get the quick fixes
let res = client . write_request (
" textDocument/codeAction " ,
json! ( {
" textDocument " : {
" uri " : main_script
} ,
" range " : {
" start " : { " line " : 0 , " character " : 16 } ,
" end " : { " line " : 0 , " character " : 18 } ,
} ,
" context " : {
" diagnostics " : json ! ( diagnostics ) ,
" only " : [ " quickfix " ]
}
} ) ,
) ;
assert_eq! (
res ,
json! ( [ {
" title " : " Update specifier to node:path " ,
" kind " : " quickfix " ,
" diagnostics " : [
{
" range " : {
" start " : { " line " : 0 , " character " : 17 } ,
" end " : { " line " : 0 , " character " : 23 }
} ,
" severity " : 2 ,
" code " : " import-node-prefix-missing " ,
" source " : " deno " ,
" message " : " \" path \" is resolved to \" node:path \" . If you want to use a built-in Node module, add a \" node: \" prefix. " ,
" data " : {
" specifier " : " path "
} ,
}
] ,
" edit " : {
" changes " : {
main_script : [
{
" range " : {
" start " : { " line " : 0 , " character " : 17 } ,
" end " : { " line " : 0 , " character " : 23 }
} ,
" newText " : " \" node:path \" "
}
]
}
}
} ] )
) ;
client . shutdown ( ) ;
}