2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2020-12-07 05:46:39 -05:00
2023-05-12 19:07:40 -04:00
use super ::analysis ::CodeActionData ;
2021-06-04 17:31:44 -04:00
use super ::code_lens ;
2021-03-15 18:01:41 -04:00
use super ::config ;
2022-03-02 16:06:38 -05:00
use super ::documents ::AssetOrDocument ;
2023-03-29 16:25:48 -04:00
use super ::documents ::DocumentsFilter ;
2021-02-06 07:39:01 -05:00
use super ::language_server ;
2020-12-21 08:44:26 -05:00
use super ::language_server ::StateSnapshot ;
2022-01-17 17:09:43 -05:00
use super ::performance ::Performance ;
2021-08-05 21:46:32 -04:00
use super ::refactor ::RefactorCodeActionData ;
use super ::refactor ::ALL_KNOWN_REFACTOR_ACTION_KINDS ;
use super ::refactor ::EXTRACT_CONSTANT ;
use super ::refactor ::EXTRACT_INTERFACE ;
use super ::refactor ::EXTRACT_TYPE ;
2021-11-22 19:08:56 -05:00
use super ::semantic_tokens ;
2021-04-19 21:26:36 -04:00
use super ::semantic_tokens ::SemanticTokensBuilder ;
2021-01-22 05:03:16 -05:00
use super ::text ::LineIndex ;
2023-03-23 10:23:04 -04:00
use super ::urls ::LspClientUrl ;
2022-01-19 17:10:14 -05:00
use super ::urls ::LspUrlMap ;
2021-07-25 01:33:42 -04:00
use super ::urls ::INVALID_SPECIFIER ;
2020-12-07 05:46:39 -05:00
2023-08-17 10:46:11 -04:00
use crate ::args ::FmtOptionsConfig ;
2022-06-27 16:54:09 -04:00
use crate ::args ::TsConfig ;
2023-07-08 16:06:45 -04:00
use crate ::cache ::HttpCache ;
use crate ::lsp ::cache ::CacheMetadata ;
use crate ::lsp ::documents ::Documents ;
2023-03-30 10:43:16 -04:00
use crate ::lsp ::logging ::lsp_warn ;
2020-12-15 14:34:39 -05:00
use crate ::tsc ;
2020-12-07 05:46:39 -05:00
use crate ::tsc ::ResolveArgs ;
2022-11-28 17:28:54 -05:00
use crate ::util ::path ::relative_specifier ;
use crate ::util ::path ::specifier_to_file_path ;
2020-12-07 05:46:39 -05:00
2023-09-19 11:37:27 -04:00
use deno_ast ::MediaType ;
2021-11-16 09:02:28 -05:00
use deno_core ::anyhow ::anyhow ;
2020-12-07 05:46:39 -05:00
use deno_core ::error ::custom_error ;
use deno_core ::error ::AnyError ;
2021-06-21 19:45:41 -04:00
use deno_core ::located_script_name ;
2023-09-12 07:14:45 -04:00
use deno_core ::op2 ;
2022-01-18 16:28:47 -05:00
use deno_core ::parking_lot ::Mutex ;
2021-02-17 13:47:18 -05:00
use deno_core ::resolve_url ;
2021-02-24 22:15:55 -05:00
use deno_core ::serde ::de ;
2020-12-07 05:46:39 -05:00
use deno_core ::serde ::Deserialize ;
2020-12-08 05:36:13 -05:00
use deno_core ::serde ::Serialize ;
2020-12-07 05:46:39 -05:00
use deno_core ::serde_json ;
use deno_core ::serde_json ::json ;
use deno_core ::serde_json ::Value ;
use deno_core ::JsRuntime ;
use deno_core ::ModuleSpecifier ;
2022-03-14 13:44:15 -04:00
use deno_core ::OpState ;
2020-12-07 05:46:39 -05:00
use deno_core ::RuntimeOptions ;
2023-09-26 16:42:39 -04:00
use deno_runtime ::deno_node ::NpmResolver ;
2021-10-21 07:05:43 -04:00
use deno_runtime ::tokio_util ::create_basic_runtime ;
2023-04-12 21:08:01 -04:00
use lazy_regex ::lazy_regex ;
2023-08-25 20:50:47 -04:00
use log ::error ;
2021-12-18 16:14:42 -05:00
use once_cell ::sync ::Lazy ;
2020-12-07 05:46:39 -05:00
use regex ::Captures ;
use regex ::Regex ;
2022-05-31 20:19:18 -04:00
use serde_repr ::Deserialize_repr ;
use serde_repr ::Serialize_repr ;
2021-10-10 17:26:22 -04:00
use std ::cmp ;
use std ::collections ::HashMap ;
2021-03-15 18:01:41 -04:00
use std ::collections ::HashSet ;
2023-05-12 19:07:40 -04:00
use std ::ops ::Range ;
2021-10-10 17:26:22 -04:00
use std ::path ::Path ;
2021-09-07 10:39:32 -04:00
use std ::sync ::Arc ;
2020-12-21 08:44:26 -05:00
use std ::thread ;
2021-10-10 17:26:22 -04:00
use text_size ::TextRange ;
use text_size ::TextSize ;
2020-12-21 08:44:26 -05:00
use tokio ::sync ::mpsc ;
use tokio ::sync ::oneshot ;
2022-02-02 09:25:22 -05:00
use tokio_util ::sync ::CancellationToken ;
2022-04-03 00:17:30 -04:00
use tower_lsp ::jsonrpc ::Error as LspError ;
use tower_lsp ::jsonrpc ::Result as LspResult ;
use tower_lsp ::lsp_types as lsp ;
2020-12-21 08:44:26 -05:00
2021-12-18 16:14:42 -05:00
static BRACKET_ACCESSOR_RE : Lazy < Regex > =
2023-04-12 21:08:01 -04:00
lazy_regex! ( r # "^\[['"](.+)[\['"]\]$"# ) ;
static CAPTION_RE : Lazy < Regex > =
lazy_regex! ( r "<caption>(.*?)</caption>\s*\r?\n((?:\s|\S)*)" ) ;
static CODEBLOCK_RE : Lazy < Regex > = lazy_regex! ( r "^\s*[~`]{3}" ) ;
static EMAIL_MATCH_RE : Lazy < Regex > = lazy_regex! ( r "(.+)\s<([-.\w]+@[-.\w]+)>" ) ;
static HTTP_RE : Lazy < Regex > = lazy_regex! ( r # "(?i)^https?:"# ) ;
static JSDOC_LINKS_RE : Lazy < Regex > = lazy_regex! (
r "(?i)\{@(link|linkplain|linkcode) (https?://[^ |}]+?)(?:[| ]([^{}\n]+?))?\}"
) ;
static PART_KIND_MODIFIER_RE : Lazy < Regex > = lazy_regex! ( r ",|\s+" ) ;
static PART_RE : Lazy < Regex > = lazy_regex! ( r "^(\S+)\s*-?\s*" ) ;
static SCOPE_RE : Lazy < Regex > = lazy_regex! ( r "scope_(\d)" ) ;
2021-09-15 22:07:52 -04:00
2021-03-15 18:01:41 -04:00
const FILE_EXTENSION_KIND_MODIFIERS : & [ & str ] =
& [ " .d.ts " , " .ts " , " .tsx " , " .js " , " .jsx " , " .json " ] ;
2020-12-21 08:44:26 -05:00
type Request = (
RequestMethod ,
2021-11-18 13:50:24 -05:00
Arc < StateSnapshot > ,
2020-12-21 08:44:26 -05:00
oneshot ::Sender < Result < Value , AnyError > > ,
2022-02-02 09:25:22 -05:00
CancellationToken ,
2020-12-21 08:44:26 -05:00
) ;
2023-08-17 10:46:11 -04:00
/// Relevant subset of https://github.com/denoland/deno/blob/80331d1fe5b85b829ac009fdc201c128b3427e11/cli/tsc/dts/typescript.d.ts#L6658.
#[ derive(Clone, Debug, Default, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct FormatCodeSettings {
convert_tabs_to_spaces : Option < bool > ,
indent_size : Option < u8 > ,
semicolons : Option < SemicolonPreference > ,
}
impl From < & FmtOptionsConfig > for FormatCodeSettings {
fn from ( config : & FmtOptionsConfig ) -> Self {
FormatCodeSettings {
convert_tabs_to_spaces : Some ( ! config . use_tabs . unwrap_or ( false ) ) ,
indent_size : Some ( config . indent_width . unwrap_or ( 2 ) ) ,
semicolons : match config . semi_colons {
Some ( false ) = > Some ( SemicolonPreference ::Remove ) ,
_ = > Some ( SemicolonPreference ::Insert ) ,
} ,
}
}
}
#[ derive(Clone, Debug, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub enum SemicolonPreference {
Insert ,
Remove ,
}
2020-12-21 08:44:26 -05:00
#[ derive(Clone, Debug) ]
pub struct TsServer ( mpsc ::UnboundedSender < Request > ) ;
impl TsServer {
2023-08-01 20:49:09 -04:00
pub fn new ( performance : Arc < Performance > , cache : Arc < dyn HttpCache > ) -> Self {
2020-12-21 08:44:26 -05:00
let ( tx , mut rx ) = mpsc ::unbounded_channel ::< Request > ( ) ;
let _join_handle = thread ::spawn ( move | | {
2023-07-08 16:06:45 -04:00
let mut ts_runtime = js_runtime ( performance , cache ) ;
2020-12-21 08:44:26 -05:00
2021-01-12 02:50:02 -05:00
let runtime = create_basic_runtime ( ) ;
2020-12-21 08:44:26 -05:00
runtime . block_on ( async {
2021-06-21 17:18:32 -04:00
let mut started = false ;
2022-02-02 09:25:22 -05:00
while let Some ( ( req , state_snapshot , tx , token ) ) = rx . recv ( ) . await {
2021-06-21 17:18:32 -04:00
if ! started {
// TODO(@kitsonk) need to reflect the debug state of the lsp here
2023-03-11 11:43:45 -05:00
start ( & mut ts_runtime , false ) . unwrap ( ) ;
2021-06-21 17:18:32 -04:00
started = true ;
}
2022-02-02 09:25:22 -05:00
let value = request ( & mut ts_runtime , state_snapshot , req , token ) ;
2020-12-21 08:44:26 -05:00
if tx . send ( value ) . is_err ( ) {
2023-03-30 10:43:16 -04:00
lsp_warn! ( " Unable to send result to client. " ) ;
2020-12-21 08:44:26 -05:00
}
}
} )
} ) ;
Self ( tx )
}
2023-05-12 19:07:40 -04:00
pub async fn get_diagnostics (
& self ,
snapshot : Arc < StateSnapshot > ,
specifiers : Vec < ModuleSpecifier > ,
token : CancellationToken ,
) -> Result < HashMap < String , Vec < crate ::tsc ::Diagnostic > > , AnyError > {
let req = RequestMethod ::GetDiagnostics ( specifiers ) ;
self . request_with_cancellation ( snapshot , req , token ) . await
}
pub async fn find_references (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < Vec < ReferencedSymbol > > , LspError > {
let req = RequestMethod ::FindReferences {
specifier ,
position ,
} ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get references from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_navigation_tree (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
) -> Result < NavigationTree , AnyError > {
self
. request ( snapshot , RequestMethod ::GetNavigationTree ( specifier ) )
. await
}
pub async fn configure (
& self ,
snapshot : Arc < StateSnapshot > ,
tsconfig : TsConfig ,
) -> Result < bool , AnyError > {
self
. request ( snapshot , RequestMethod ::Configure ( tsconfig ) )
. await
}
pub async fn get_supported_code_fixes (
& self ,
snapshot : Arc < StateSnapshot > ,
) -> Result < Vec < String > , LspError > {
self
. request ( snapshot , RequestMethod ::GetSupportedCodeFixes )
. await
. map_err ( | err | {
log ::error! ( " Unable to get fixable diagnostics: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_quick_info (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < QuickInfo > , LspError > {
let req = RequestMethod ::GetQuickInfo ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get quick info: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_code_fixes (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
range : Range < u32 > ,
codes : Vec < String > ,
2023-08-17 10:46:11 -04:00
format_code_settings : FormatCodeSettings ,
2023-09-18 15:48:32 -04:00
preferences : UserPreferences ,
2023-05-12 19:07:40 -04:00
) -> Vec < CodeFixAction > {
2023-08-17 10:46:11 -04:00
let req = RequestMethod ::GetCodeFixes ( (
specifier ,
range . start ,
range . end ,
codes ,
format_code_settings ,
2023-09-18 15:48:32 -04:00
preferences ,
2023-08-17 10:46:11 -04:00
) ) ;
2023-05-12 19:07:40 -04:00
match self . request ( snapshot , req ) . await {
Ok ( items ) = > items ,
Err ( err ) = > {
// sometimes tsc reports errors when retrieving code actions
// because they don't reflect the current state of the document
// so we will log them to the output, but we won't send an error
// message back to the client.
log ::error! ( " Error getting actions from TypeScript: {} " , err ) ;
Vec ::new ( )
}
}
}
pub async fn get_applicable_refactors (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
range : Range < u32 > ,
2023-09-18 15:48:32 -04:00
preferences : Option < UserPreferences > ,
2023-05-12 19:07:40 -04:00
only : String ,
) -> Result < Vec < ApplicableRefactorInfo > , LspError > {
let req = RequestMethod ::GetApplicableRefactors ( (
specifier . clone ( ) ,
TextSpan {
start : range . start ,
length : range . end - range . start ,
} ,
2023-09-18 15:48:32 -04:00
preferences ,
2023-05-12 19:07:40 -04:00
only ,
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_combined_code_fix (
& self ,
snapshot : Arc < StateSnapshot > ,
code_action_data : & CodeActionData ,
2023-08-17 10:46:11 -04:00
format_code_settings : FormatCodeSettings ,
2023-09-18 15:48:32 -04:00
preferences : UserPreferences ,
2023-05-12 19:07:40 -04:00
) -> Result < CombinedCodeActions , LspError > {
let req = RequestMethod ::GetCombinedCodeFix ( (
code_action_data . specifier . clone ( ) ,
json! ( code_action_data . fix_id . clone ( ) ) ,
2023-08-17 10:46:11 -04:00
format_code_settings ,
2023-09-18 15:48:32 -04:00
preferences ,
2023-05-12 19:07:40 -04:00
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get combined fix from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} )
}
2023-09-18 15:48:32 -04:00
#[ allow(clippy::too_many_arguments) ]
2023-05-12 19:07:40 -04:00
pub async fn get_edits_for_refactor (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
2023-08-17 10:46:11 -04:00
format_code_settings : FormatCodeSettings ,
2023-05-12 19:07:40 -04:00
range : Range < u32 > ,
refactor_name : String ,
action_name : String ,
2023-09-18 15:48:32 -04:00
preferences : Option < UserPreferences > ,
2023-05-12 19:07:40 -04:00
) -> Result < RefactorEditInfo , LspError > {
let req = RequestMethod ::GetEditsForRefactor ( (
specifier ,
2023-08-17 10:46:11 -04:00
format_code_settings ,
2023-05-12 19:07:40 -04:00
TextSpan {
start : range . start ,
length : range . end - range . start ,
} ,
refactor_name ,
action_name ,
2023-09-18 15:48:32 -04:00
preferences ,
2023-05-12 19:07:40 -04:00
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
2023-08-25 20:50:47 -04:00
pub async fn get_edits_for_file_rename (
& self ,
snapshot : Arc < StateSnapshot > ,
old_specifier : ModuleSpecifier ,
new_specifier : ModuleSpecifier ,
format_code_settings : FormatCodeSettings ,
user_preferences : UserPreferences ,
) -> Result < Vec < FileTextChanges > , LspError > {
let req = RequestMethod ::GetEditsForFileRename ( (
old_specifier ,
new_specifier ,
format_code_settings ,
user_preferences ,
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
2023-05-12 19:07:40 -04:00
pub async fn get_document_highlights (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
files_to_search : Vec < ModuleSpecifier > ,
) -> Result < Option < Vec < DocumentHighlights > > , LspError > {
let req = RequestMethod ::GetDocumentHighlights ( (
specifier ,
position ,
files_to_search ,
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get document highlights from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_definition (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < DefinitionInfoAndBoundSpan > , LspError > {
let req = RequestMethod ::GetDefinition ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get definition from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_type_definition (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < Vec < DefinitionInfo > > , LspError > {
let req = RequestMethod ::GetTypeDefinition {
specifier ,
position ,
} ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get type definition from TypeScript: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn get_completions (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
options : GetCompletionsAtPositionOptions ,
2023-08-17 10:46:11 -04:00
format_code_settings : FormatCodeSettings ,
2023-05-12 19:07:40 -04:00
) -> Option < CompletionInfo > {
2023-08-17 10:46:11 -04:00
let req = RequestMethod ::GetCompletions ( (
specifier ,
position ,
options ,
format_code_settings ,
) ) ;
2023-05-12 19:07:40 -04:00
match self . request ( snapshot , req ) . await {
Ok ( maybe_info ) = > maybe_info ,
Err ( err ) = > {
log ::error! ( " Unable to get completion info from TypeScript: {:#} " , err ) ;
None
}
}
}
pub async fn get_completion_details (
& self ,
snapshot : Arc < StateSnapshot > ,
args : GetCompletionDetailsArgs ,
) -> Result < Option < CompletionEntryDetails > , AnyError > {
let req = RequestMethod ::GetCompletionDetails ( args ) ;
self . request ( snapshot , req ) . await
}
pub async fn get_implementations (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < Vec < ImplementationLocation > > , LspError > {
let req = RequestMethod ::GetImplementation ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_outlining_spans (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
) -> Result < Vec < OutliningSpan > , LspError > {
let req = RequestMethod ::GetOutliningSpans ( specifier ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn provide_call_hierarchy_incoming_calls (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Vec < CallHierarchyIncomingCall > , LspError > {
let req =
RequestMethod ::ProvideCallHierarchyIncomingCalls ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn provide_call_hierarchy_outgoing_calls (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Vec < CallHierarchyOutgoingCall > , LspError > {
let req =
RequestMethod ::ProvideCallHierarchyOutgoingCalls ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn prepare_call_hierarchy (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < OneOrMany < CallHierarchyItem > > , LspError > {
let req = RequestMethod ::PrepareCallHierarchy ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn find_rename_locations (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < Option < Vec < RenameLocation > > , LspError > {
let req = RequestMethod ::FindRenameLocations {
specifier ,
position ,
find_in_strings : false ,
find_in_comments : false ,
provide_prefix_and_suffix_text_for_rename : false ,
} ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_smart_selection_range (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
) -> Result < SelectionRange , LspError > {
let req = RequestMethod ::GetSmartSelectionRange ( ( specifier , position ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_encoded_semantic_classifications (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
range : Range < u32 > ,
) -> Result < Classifications , LspError > {
let req = RequestMethod ::GetEncodedSemanticClassifications ( (
specifier ,
TextSpan {
start : range . start ,
length : range . end - range . start ,
} ,
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_signature_help_items (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
position : u32 ,
options : SignatureHelpItemsOptions ,
) -> Result < Option < SignatureHelpItems > , LspError > {
let req =
RequestMethod ::GetSignatureHelpItems ( ( specifier , position , options ) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed to request to tsserver: {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn get_navigate_to_items (
& self ,
snapshot : Arc < StateSnapshot > ,
args : GetNavigateToItemsArgs ,
) -> Result < Vec < NavigateToItem > , LspError > {
let req = RequestMethod ::GetNavigateToItems ( args ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Failed request to tsserver: {} " , err ) ;
LspError ::invalid_request ( )
} )
}
pub async fn provide_inlay_hints (
& self ,
snapshot : Arc < StateSnapshot > ,
specifier : ModuleSpecifier ,
text_span : TextSpan ,
user_preferences : UserPreferences ,
) -> Result < Option < Vec < InlayHint > > , LspError > {
let req = RequestMethod ::ProvideInlayHints ( (
specifier ,
text_span ,
user_preferences ,
) ) ;
self . request ( snapshot , req ) . await . map_err ( | err | {
log ::error! ( " Unable to get inlay hints: {} " , err ) ;
LspError ::internal_error ( )
} )
}
pub async fn restart ( & self , snapshot : Arc < StateSnapshot > ) {
let _ : bool = self
. request ( snapshot , RequestMethod ::Restart )
. await
. unwrap ( ) ;
}
async fn request < R > (
2020-12-21 08:44:26 -05:00
& self ,
2021-11-18 13:50:24 -05:00
snapshot : Arc < StateSnapshot > ,
2020-12-21 08:44:26 -05:00
req : RequestMethod ,
2021-02-24 22:15:55 -05:00
) -> Result < R , AnyError >
2022-02-02 09:25:22 -05:00
where
R : de ::DeserializeOwned ,
{
self
. request_with_cancellation ( snapshot , req , Default ::default ( ) )
. await
}
2023-05-12 19:07:40 -04:00
async fn request_with_cancellation < R > (
2022-02-02 09:25:22 -05:00
& self ,
snapshot : Arc < StateSnapshot > ,
req : RequestMethod ,
token : CancellationToken ,
) -> Result < R , AnyError >
2021-02-24 22:15:55 -05:00
where
R : de ::DeserializeOwned ,
{
2020-12-21 08:44:26 -05:00
let ( tx , rx ) = oneshot ::channel ::< Result < Value , AnyError > > ( ) ;
2022-02-02 09:25:22 -05:00
if self . 0. send ( ( req , snapshot , tx , token ) ) . is_err ( ) {
2020-12-21 08:44:26 -05:00
return Err ( anyhow! ( " failed to send request to tsc thread " ) ) ;
}
2023-03-30 12:15:21 -04:00
let value = rx . await ? ? ;
Ok ( serde_json ::from_value ::< R > ( value ) ? )
}
2020-12-21 08:44:26 -05:00
}
2020-12-07 05:46:39 -05:00
2021-11-12 11:42:04 -05:00
#[ derive(Debug, Clone) ]
struct AssetDocumentInner {
2022-03-02 16:06:38 -05:00
specifier : ModuleSpecifier ,
2022-05-20 16:40:55 -04:00
text : Arc < str > ,
2021-11-12 11:42:04 -05:00
line_index : Arc < LineIndex > ,
maybe_navigation_tree : Option < Arc < NavigationTree > > ,
}
2021-01-22 05:03:16 -05:00
/// An lsp representation of an asset in memory, that has either been retrieved
/// from static assets built into Rust, or static assets built into tsc.
#[ derive(Debug, Clone) ]
2021-11-12 11:42:04 -05:00
pub struct AssetDocument ( Arc < AssetDocumentInner > ) ;
2021-01-22 05:03:16 -05:00
2021-02-12 06:49:42 -05:00
impl AssetDocument {
2022-03-02 16:06:38 -05:00
pub fn new ( specifier : ModuleSpecifier , text : impl AsRef < str > ) -> Self {
2021-02-12 06:49:42 -05:00
let text = text . as_ref ( ) ;
2021-11-12 11:42:04 -05:00
Self ( Arc ::new ( AssetDocumentInner {
2022-03-02 16:06:38 -05:00
specifier ,
2022-05-20 16:40:55 -04:00
text : text . into ( ) ,
2021-10-28 19:56:01 -04:00
line_index : Arc ::new ( LineIndex ::new ( text ) ) ,
2021-06-04 17:31:44 -04:00
maybe_navigation_tree : None ,
2021-11-12 11:42:04 -05:00
} ) )
}
2022-03-02 16:06:38 -05:00
pub fn specifier ( & self ) -> & ModuleSpecifier {
& self . 0. specifier
}
2021-11-12 11:42:04 -05:00
pub fn with_navigation_tree (
& self ,
tree : Arc < NavigationTree > ,
) -> AssetDocument {
AssetDocument ( Arc ::new ( AssetDocumentInner {
maybe_navigation_tree : Some ( tree ) ,
.. ( * self . 0 ) . clone ( )
} ) )
}
2022-05-20 16:40:55 -04:00
pub fn text ( & self ) -> Arc < str > {
2021-11-12 11:42:04 -05:00
self . 0. text . clone ( )
}
pub fn line_index ( & self ) -> Arc < LineIndex > {
self . 0. line_index . clone ( )
}
pub fn maybe_navigation_tree ( & self ) -> Option < Arc < NavigationTree > > {
self . 0. maybe_navigation_tree . clone ( )
2021-02-12 06:49:42 -05:00
}
}
2022-04-25 11:23:24 -04:00
type AssetsMap = HashMap < ModuleSpecifier , AssetDocument > ;
2022-01-18 16:28:47 -05:00
fn new_assets_map ( ) -> Arc < Mutex < AssetsMap > > {
2023-01-14 09:36:19 -05:00
let assets = tsc ::LAZILY_LOADED_STATIC_ASSETS
2022-01-18 16:28:47 -05:00
. iter ( )
. map ( | ( k , v ) | {
2023-01-27 10:43:16 -05:00
let url_str = format! ( " asset:/// {k} " ) ;
2022-01-18 16:28:47 -05:00
let specifier = resolve_url ( & url_str ) . unwrap ( ) ;
2022-03-02 16:06:38 -05:00
let asset = AssetDocument ::new ( specifier . clone ( ) , v ) ;
2022-04-25 11:23:24 -04:00
( specifier , asset )
2022-01-18 16:28:47 -05:00
} )
2023-01-24 09:05:54 -05:00
. collect ::< AssetsMap > ( ) ;
2022-01-18 16:28:47 -05:00
Arc ::new ( Mutex ::new ( assets ) )
}
/// Snapshot of Assets.
2021-02-12 06:49:42 -05:00
#[ derive(Debug, Clone) ]
2022-01-18 16:28:47 -05:00
pub struct AssetsSnapshot ( Arc < Mutex < AssetsMap > > ) ;
2021-02-12 06:49:42 -05:00
2022-01-18 16:28:47 -05:00
impl Default for AssetsSnapshot {
2021-02-12 06:49:42 -05:00
fn default ( ) -> Self {
2022-01-18 16:28:47 -05:00
Self ( new_assets_map ( ) )
2021-02-12 06:49:42 -05:00
}
}
2022-01-18 16:28:47 -05:00
impl AssetsSnapshot {
2021-02-12 06:49:42 -05:00
pub fn contains_key ( & self , k : & ModuleSpecifier ) -> bool {
2022-01-18 16:28:47 -05:00
self . 0. lock ( ) . contains_key ( k )
}
2022-04-25 11:23:24 -04:00
pub fn get ( & self , k : & ModuleSpecifier ) -> Option < AssetDocument > {
2022-01-18 16:28:47 -05:00
self . 0. lock ( ) . get ( k ) . cloned ( )
}
}
/// Assets are never updated and so we can safely use this struct across
/// multiple threads without needing to worry about race conditions.
#[ derive(Debug, Clone) ]
pub struct Assets {
ts_server : Arc < TsServer > ,
assets : Arc < Mutex < AssetsMap > > ,
}
impl Assets {
pub fn new ( ts_server : Arc < TsServer > ) -> Self {
Self {
ts_server ,
assets : new_assets_map ( ) ,
}
2021-02-12 06:49:42 -05:00
}
2022-04-25 11:23:24 -04:00
/// Initializes with the assets in the isolate.
2023-06-26 09:10:27 -04:00
pub async fn initialize ( & self , state_snapshot : Arc < StateSnapshot > ) {
2022-04-25 11:23:24 -04:00
let assets = get_isolate_assets ( & self . ts_server , state_snapshot ) . await ;
let mut assets_map = self . assets . lock ( ) ;
for asset in assets {
if ! assets_map . contains_key ( asset . specifier ( ) ) {
assets_map . insert ( asset . specifier ( ) . clone ( ) , asset ) ;
}
}
}
2022-01-18 16:28:47 -05:00
pub fn snapshot ( & self ) -> AssetsSnapshot {
// it's ok to not make a complete copy for snapshotting purposes
// because assets are static
AssetsSnapshot ( self . assets . clone ( ) )
2021-02-12 06:49:42 -05:00
}
2022-04-25 11:23:24 -04:00
pub fn get ( & self , specifier : & ModuleSpecifier ) -> Option < AssetDocument > {
self . assets . lock ( ) . get ( specifier ) . cloned ( )
2021-02-12 06:49:42 -05:00
}
2021-06-04 17:31:44 -04:00
2021-11-18 13:50:24 -05:00
pub fn cache_navigation_tree (
2022-01-18 16:28:47 -05:00
& self ,
2021-06-04 17:31:44 -04:00
specifier : & ModuleSpecifier ,
2021-10-28 19:56:01 -04:00
navigation_tree : Arc < NavigationTree > ,
2021-06-04 17:31:44 -04:00
) -> Result < ( ) , AnyError > {
2022-01-18 16:28:47 -05:00
let mut assets = self . assets . lock ( ) ;
2022-04-25 11:23:24 -04:00
let doc = assets
2021-06-04 17:31:44 -04:00
. get_mut ( specifier )
. ok_or_else ( | | anyhow! ( " Missing asset. " ) ) ? ;
2021-11-12 11:42:04 -05:00
* doc = doc . with_navigation_tree ( navigation_tree ) ;
2021-06-04 17:31:44 -04:00
Ok ( ( ) )
}
2021-02-12 06:49:42 -05:00
}
2022-04-25 11:23:24 -04:00
/// Get all the assets stored in the tsc isolate.
async fn get_isolate_assets (
2020-12-21 08:44:26 -05:00
ts_server : & TsServer ,
2021-11-18 13:50:24 -05:00
state_snapshot : Arc < StateSnapshot > ,
2022-04-25 11:23:24 -04:00
) -> Vec < AssetDocument > {
let res : Value = ts_server
. request ( state_snapshot , RequestMethod ::GetAssets )
. await
. unwrap ( ) ;
let response_assets = match res {
Value ::Array ( value ) = > value ,
_ = > unreachable! ( ) ,
} ;
let mut assets = Vec ::with_capacity ( response_assets . len ( ) ) ;
for asset in response_assets {
let mut obj = match asset {
Value ::Object ( obj ) = > obj ,
_ = > unreachable! ( ) ,
} ;
let specifier_str = obj . get ( " specifier " ) . unwrap ( ) . as_str ( ) . unwrap ( ) ;
let specifier = ModuleSpecifier ::parse ( specifier_str ) . unwrap ( ) ;
let text = match obj . remove ( " text " ) . unwrap ( ) {
Value ::String ( text ) = > text ,
_ = > unreachable! ( ) ,
} ;
assets . push ( AssetDocument ::new ( specifier , text ) ) ;
2020-12-07 05:46:39 -05:00
}
2022-04-25 11:23:24 -04:00
assets
2020-12-07 05:46:39 -05:00
}
2022-02-09 18:08:53 -05:00
fn get_tag_body_text (
tag : & JsDocTagInfo ,
language_server : & language_server ::Inner ,
) -> Option < String > {
2021-05-27 19:33:11 -04:00
tag . text . as_ref ( ) . map ( | display_parts | {
// TODO(@kitsonk) check logic in vscode about handling this API change in
// tsserver
2022-02-09 18:08:53 -05:00
let text = display_parts_to_string ( display_parts , language_server ) ;
2021-05-27 19:33:11 -04:00
match tag . name . as_str ( ) {
" example " = > {
2021-09-15 22:07:52 -04:00
if CAPTION_RE . is_match ( & text ) {
CAPTION_RE
2021-05-27 19:33:11 -04:00
. replace ( & text , | c : & Captures | {
format! ( " {} \n \n {} " , & c [ 1 ] , make_codeblock ( & c [ 2 ] ) )
} )
. to_string ( )
} else {
make_codeblock ( & text )
}
}
2021-09-15 22:07:52 -04:00
" author " = > EMAIL_MATCH_RE
. replace ( & text , | c : & Captures | format! ( " {} {} " , & c [ 1 ] , & c [ 2 ] ) )
. to_string ( ) ,
2021-05-27 19:33:11 -04:00
" default " = > make_codeblock ( & text ) ,
_ = > replace_links ( & text ) ,
2020-12-07 05:46:39 -05:00
}
} )
}
2022-02-09 18:08:53 -05:00
fn get_tag_documentation (
tag : & JsDocTagInfo ,
language_server : & language_server ::Inner ,
) -> String {
2020-12-07 05:46:39 -05:00
match tag . name . as_str ( ) {
" augments " | " extends " | " param " | " template " = > {
2021-05-27 19:33:11 -04:00
if let Some ( display_parts ) = & tag . text {
// TODO(@kitsonk) check logic in vscode about handling this API change
// in tsserver
2022-02-09 18:08:53 -05:00
let text = display_parts_to_string ( display_parts , language_server ) ;
2021-09-15 22:07:52 -04:00
let body : Vec < & str > = PART_RE . split ( & text ) . collect ( ) ;
2020-12-07 05:46:39 -05:00
if body . len ( ) = = 3 {
let param = body [ 1 ] ;
let doc = body [ 2 ] ;
let label = format! ( " *@ {} * ` {} ` " , tag . name , param ) ;
if doc . is_empty ( ) {
return label ;
}
if doc . contains ( '\n' ) {
return format! ( " {} \n {} " , label , replace_links ( doc ) ) ;
} else {
return format! ( " {} - {} " , label , replace_links ( doc ) ) ;
}
}
}
}
_ = > ( ) ,
}
let label = format! ( " *@ {} * " , tag . name ) ;
2022-02-09 18:08:53 -05:00
let maybe_text = get_tag_body_text ( tag , language_server ) ;
2020-12-07 05:46:39 -05:00
if let Some ( text ) = maybe_text {
if text . contains ( '\n' ) {
2023-01-27 10:43:16 -05:00
format! ( " {label} \n {text} " )
2020-12-07 05:46:39 -05:00
} else {
2023-01-27 10:43:16 -05:00
format! ( " {label} - {text} " )
2020-12-07 05:46:39 -05:00
}
} else {
label
}
}
fn make_codeblock ( text : & str ) -> String {
2021-09-15 22:07:52 -04:00
if CODEBLOCK_RE . is_match ( text ) {
2020-12-07 05:46:39 -05:00
text . to_string ( )
} else {
2023-01-27 10:43:16 -05:00
format! ( " ``` \n {text} \n ``` " )
2020-12-07 05:46:39 -05:00
}
}
/// Replace JSDoc like links (`{@link http://example.com}`) with markdown links
2022-02-09 18:08:53 -05:00
fn replace_links < S : AsRef < str > > ( text : S ) -> String {
2021-09-15 22:07:52 -04:00
JSDOC_LINKS_RE
2022-02-09 18:08:53 -05:00
. replace_all ( text . as_ref ( ) , | c : & Captures | match & c [ 1 ] {
2020-12-07 05:46:39 -05:00
" linkcode " = > format! (
" [`{}`]({}) " ,
if c . get ( 3 ) . is_none ( ) {
& c [ 2 ]
} else {
c [ 3 ] . trim ( )
} ,
& c [ 2 ]
) ,
_ = > format! (
" [{}]({}) " ,
if c . get ( 3 ) . is_none ( ) {
& c [ 2 ]
} else {
c [ 3 ] . trim ( )
} ,
& c [ 2 ]
) ,
} )
. to_string ( )
}
2021-03-15 18:01:41 -04:00
fn parse_kind_modifier ( kind_modifiers : & str ) -> HashSet < & str > {
2021-09-15 22:07:52 -04:00
PART_KIND_MODIFIER_RE . split ( kind_modifiers ) . collect ( )
2021-03-15 18:01:41 -04:00
}
2021-04-19 01:11:26 -04:00
#[ derive(Debug, Deserialize) ]
#[ serde(untagged) ]
pub enum OneOrMany < T > {
One ( T ) ,
Many ( Vec < T > ) ,
}
2021-03-15 18:01:41 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2020-12-07 05:46:39 -05:00
pub enum ScriptElementKind {
#[ serde(rename = " " ) ]
Unknown ,
#[ serde(rename = " warning " ) ]
Warning ,
#[ serde(rename = " keyword " ) ]
Keyword ,
#[ serde(rename = " script " ) ]
ScriptElement ,
#[ serde(rename = " module " ) ]
ModuleElement ,
#[ serde(rename = " class " ) ]
ClassElement ,
#[ serde(rename = " local class " ) ]
LocalClassElement ,
#[ serde(rename = " interface " ) ]
InterfaceElement ,
#[ serde(rename = " type " ) ]
TypeElement ,
#[ serde(rename = " enum " ) ]
EnumElement ,
#[ serde(rename = " enum member " ) ]
EnumMemberElement ,
#[ serde(rename = " var " ) ]
VariableElement ,
#[ serde(rename = " local var " ) ]
LocalVariableElement ,
#[ serde(rename = " function " ) ]
FunctionElement ,
#[ serde(rename = " local function " ) ]
LocalFunctionElement ,
#[ serde(rename = " method " ) ]
MemberFunctionElement ,
#[ serde(rename = " getter " ) ]
MemberGetAccessorElement ,
#[ serde(rename = " setter " ) ]
MemberSetAccessorElement ,
#[ serde(rename = " property " ) ]
MemberVariableElement ,
#[ serde(rename = " constructor " ) ]
ConstructorImplementationElement ,
#[ serde(rename = " call " ) ]
CallSignatureElement ,
#[ serde(rename = " index " ) ]
IndexSignatureElement ,
#[ serde(rename = " construct " ) ]
ConstructSignatureElement ,
#[ serde(rename = " parameter " ) ]
ParameterElement ,
#[ serde(rename = " type parameter " ) ]
TypeParameterElement ,
#[ serde(rename = " primitive type " ) ]
PrimitiveType ,
#[ serde(rename = " label " ) ]
Label ,
#[ serde(rename = " alias " ) ]
Alias ,
#[ serde(rename = " const " ) ]
ConstElement ,
#[ serde(rename = " let " ) ]
LetElement ,
#[ serde(rename = " directory " ) ]
Directory ,
#[ serde(rename = " external module name " ) ]
ExternalModuleName ,
#[ serde(rename = " JSX attribute " ) ]
JsxAttribute ,
#[ serde(rename = " string " ) ]
String ,
2021-11-09 05:45:40 -05:00
#[ serde(rename = " link " ) ]
Link ,
#[ serde(rename = " link name " ) ]
LinkName ,
2022-07-11 19:35:18 -04:00
#[ serde(rename = " link text " ) ]
2021-11-09 05:45:40 -05:00
LinkText ,
2020-12-07 05:46:39 -05:00
}
2021-03-15 18:01:41 -04:00
impl Default for ScriptElementKind {
fn default ( ) -> Self {
Self ::Unknown
}
}
2021-11-09 05:45:40 -05:00
/// This mirrors the method `convertKind` in `completions.ts` in vscode
2021-01-29 14:34:33 -05:00
impl From < ScriptElementKind > for lsp ::CompletionItemKind {
2020-12-08 05:36:13 -05:00
fn from ( kind : ScriptElementKind ) -> Self {
match kind {
ScriptElementKind ::PrimitiveType | ScriptElementKind ::Keyword = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::KEYWORD
2020-12-08 05:36:13 -05:00
}
2021-03-15 18:01:41 -04:00
ScriptElementKind ::ConstElement
| ScriptElementKind ::LetElement
2020-12-08 05:36:13 -05:00
| ScriptElementKind ::VariableElement
| ScriptElementKind ::LocalVariableElement
2021-03-15 18:01:41 -04:00
| ScriptElementKind ::Alias
| ScriptElementKind ::ParameterElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::VARIABLE
2021-03-15 18:01:41 -04:00
}
2020-12-08 05:36:13 -05:00
ScriptElementKind ::MemberVariableElement
| ScriptElementKind ::MemberGetAccessorElement
| ScriptElementKind ::MemberSetAccessorElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::FIELD
2021-03-15 18:01:41 -04:00
}
ScriptElementKind ::FunctionElement
| ScriptElementKind ::LocalFunctionElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::FUNCTION
2020-12-08 05:36:13 -05:00
}
ScriptElementKind ::MemberFunctionElement
| ScriptElementKind ::ConstructSignatureElement
| ScriptElementKind ::CallSignatureElement
2021-03-15 18:01:41 -04:00
| ScriptElementKind ::IndexSignatureElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::METHOD
2021-03-15 18:01:41 -04:00
}
2021-11-24 20:10:12 -05:00
ScriptElementKind ::EnumElement = > lsp ::CompletionItemKind ::ENUM ,
2021-03-15 18:01:41 -04:00
ScriptElementKind ::EnumMemberElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::ENUM_MEMBER
2021-03-15 18:01:41 -04:00
}
2020-12-08 05:36:13 -05:00
ScriptElementKind ::ModuleElement
2021-03-15 18:01:41 -04:00
| ScriptElementKind ::ExternalModuleName = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::MODULE
2020-12-08 05:36:13 -05:00
}
2021-03-15 18:01:41 -04:00
ScriptElementKind ::ClassElement | ScriptElementKind ::TypeElement = > {
2021-11-24 20:10:12 -05:00
lsp ::CompletionItemKind ::CLASS
2020-12-08 05:36:13 -05:00
}
2021-11-24 20:10:12 -05:00
ScriptElementKind ::InterfaceElement = > lsp ::CompletionItemKind ::INTERFACE ,
ScriptElementKind ::Warning = > lsp ::CompletionItemKind ::TEXT ,
ScriptElementKind ::ScriptElement = > lsp ::CompletionItemKind ::FILE ,
ScriptElementKind ::Directory = > lsp ::CompletionItemKind ::FOLDER ,
ScriptElementKind ::String = > lsp ::CompletionItemKind ::CONSTANT ,
_ = > lsp ::CompletionItemKind ::PROPERTY ,
2020-12-08 05:36:13 -05:00
}
}
}
2021-11-09 05:45:40 -05:00
/// This mirrors `fromProtocolScriptElementKind` in vscode
2021-04-19 01:11:26 -04:00
impl From < ScriptElementKind > for lsp ::SymbolKind {
fn from ( kind : ScriptElementKind ) -> Self {
match kind {
2021-11-24 20:10:12 -05:00
ScriptElementKind ::ModuleElement = > Self ::MODULE ,
2021-11-22 19:08:56 -05:00
// this is only present in `getSymbolKind` in `workspaceSymbols` in
// vscode, but seems strange it isn't consistent.
2021-11-24 20:10:12 -05:00
ScriptElementKind ::TypeElement = > Self ::CLASS ,
ScriptElementKind ::ClassElement = > Self ::CLASS ,
ScriptElementKind ::EnumElement = > Self ::ENUM ,
ScriptElementKind ::EnumMemberElement = > Self ::ENUM_MEMBER ,
ScriptElementKind ::InterfaceElement = > Self ::INTERFACE ,
ScriptElementKind ::IndexSignatureElement = > Self ::METHOD ,
ScriptElementKind ::CallSignatureElement = > Self ::METHOD ,
ScriptElementKind ::MemberFunctionElement = > Self ::METHOD ,
2021-11-22 19:08:56 -05:00
// workspaceSymbols in vscode treats them as fields, which does seem more
// semantically correct while `fromProtocolScriptElementKind` treats them
// as properties.
2021-11-24 20:10:12 -05:00
ScriptElementKind ::MemberVariableElement = > Self ::FIELD ,
ScriptElementKind ::MemberGetAccessorElement = > Self ::FIELD ,
ScriptElementKind ::MemberSetAccessorElement = > Self ::FIELD ,
ScriptElementKind ::VariableElement = > Self ::VARIABLE ,
ScriptElementKind ::LetElement = > Self ::VARIABLE ,
ScriptElementKind ::ConstElement = > Self ::VARIABLE ,
ScriptElementKind ::LocalVariableElement = > Self ::VARIABLE ,
ScriptElementKind ::Alias = > Self ::VARIABLE ,
ScriptElementKind ::FunctionElement = > Self ::FUNCTION ,
ScriptElementKind ::LocalFunctionElement = > Self ::FUNCTION ,
ScriptElementKind ::ConstructSignatureElement = > Self ::CONSTRUCTOR ,
ScriptElementKind ::ConstructorImplementationElement = > Self ::CONSTRUCTOR ,
ScriptElementKind ::TypeParameterElement = > Self ::TYPE_PARAMETER ,
ScriptElementKind ::String = > Self ::STRING ,
_ = > Self ::VARIABLE ,
2021-04-19 01:11:26 -04:00
}
}
}
2021-02-04 13:53:02 -05:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2020-12-07 05:46:39 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct TextSpan {
2021-01-31 22:30:41 -05:00
pub start : u32 ,
pub length : u32 ,
2020-12-07 05:46:39 -05:00
}
impl TextSpan {
2022-10-15 22:39:43 -04:00
pub fn from_range (
range : & lsp ::Range ,
line_index : Arc < LineIndex > ,
) -> Result < Self , AnyError > {
let start = line_index . offset_tsc ( range . start ) ? ;
let length = line_index . offset_tsc ( range . end ) ? - start ;
Ok ( Self { start , length } )
}
2021-10-28 19:56:01 -04:00
pub fn to_range ( & self , line_index : Arc < LineIndex > ) -> lsp ::Range {
2021-01-29 14:34:33 -05:00
lsp ::Range {
2021-01-22 05:03:16 -05:00
start : line_index . position_tsc ( self . start . into ( ) ) ,
end : line_index . position_tsc ( TextSize ::from ( self . start + self . length ) ) ,
2020-12-07 05:46:39 -05:00
}
}
}
2022-05-31 20:19:18 -04:00
#[ derive(Debug, Serialize, Deserialize, Clone) ]
2020-12-07 05:46:39 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct SymbolDisplayPart {
text : String ,
kind : String ,
2022-02-09 18:08:53 -05:00
// This is only on `JSDocLinkDisplayPart` which extends `SymbolDisplayPart`
// but is only used as an upcast of a `SymbolDisplayPart` and not explicitly
// returned by any API, so it is safe to add it as an optional value.
target : Option < DocumentSpan > ,
2020-12-07 05:46:39 -05:00
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
2020-12-07 05:46:39 -05:00
#[ serde(rename_all = " camelCase " ) ]
2021-03-25 14:17:37 -04:00
pub struct JsDocTagInfo {
2020-12-07 05:46:39 -05:00
name : String ,
2021-05-27 19:33:11 -04:00
text : Option < Vec < SymbolDisplayPart > > ,
2020-12-07 05:46:39 -05:00
}
2022-02-23 16:01:20 -05:00
// Note: the tsc protocol contains fields that are part of the protocol but
// not currently used. They are commented out in the structures so it is clear
// that they exist.
2020-12-07 05:46:39 -05:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct QuickInfo {
2022-02-23 16:01:20 -05:00
// kind: ScriptElementKind,
// kind_modifiers: String,
2020-12-07 05:46:39 -05:00
text_span : TextSpan ,
display_parts : Option < Vec < SymbolDisplayPart > > ,
documentation : Option < Vec < SymbolDisplayPart > > ,
2021-03-25 14:17:37 -04:00
tags : Option < Vec < JsDocTagInfo > > ,
2020-12-07 05:46:39 -05:00
}
2022-02-09 18:08:53 -05:00
#[ derive(Default) ]
struct Link {
name : Option < String > ,
target : Option < DocumentSpan > ,
text : Option < String > ,
linkcode : bool ,
}
/// Takes `SymbolDisplayPart` items and converts them into a string, handling
/// any `{@link Symbol}` and `{@linkcode Symbol}` JSDoc tags and linking them
/// to the their source location.
fn display_parts_to_string (
parts : & [ SymbolDisplayPart ] ,
language_server : & language_server ::Inner ,
) -> String {
let mut out = Vec ::< String > ::new ( ) ;
let mut current_link : Option < Link > = None ;
for part in parts {
match part . kind . as_str ( ) {
" link " = > {
if let Some ( link ) = current_link . as_mut ( ) {
if let Some ( target ) = & link . target {
if let Some ( specifier ) = target . to_target ( language_server ) {
let link_text = link . text . clone ( ) . unwrap_or_else ( | | {
link
. name
. clone ( )
. map ( | ref n | n . replace ( '`' , " \\ ` " ) )
. unwrap_or_else ( | | " " . to_string ( ) )
} ) ;
let link_str = if link . linkcode {
2023-01-27 10:43:16 -05:00
format! ( " [` {link_text} `]( {specifier} ) " )
2022-02-09 18:08:53 -05:00
} else {
2023-01-27 10:43:16 -05:00
format! ( " [ {link_text} ]( {specifier} ) " )
2022-02-09 18:08:53 -05:00
} ;
out . push ( link_str ) ;
}
} else {
let maybe_text = link . text . clone ( ) . or_else ( | | link . name . clone ( ) ) ;
if let Some ( text ) = maybe_text {
if HTTP_RE . is_match ( & text ) {
let parts : Vec < & str > = text . split ( ' ' ) . collect ( ) ;
if parts . len ( ) = = 1 {
out . push ( parts [ 0 ] . to_string ( ) ) ;
} else {
let link_text = parts [ 1 .. ] . join ( " " ) . replace ( '`' , " \\ ` " ) ;
let link_str = if link . linkcode {
format! ( " [` {} `]( {} ) " , link_text , parts [ 0 ] )
} else {
format! ( " [ {} ]( {} ) " , link_text , parts [ 0 ] )
} ;
out . push ( link_str ) ;
}
} else {
out . push ( text . replace ( '`' , " \\ ` " ) ) ;
}
}
}
current_link = None ;
} else {
current_link = Some ( Link {
linkcode : part . text . as_str ( ) = = " {@linkcode " ,
.. Default ::default ( )
} ) ;
}
}
" linkName " = > {
if let Some ( link ) = current_link . as_mut ( ) {
link . name = Some ( part . text . clone ( ) ) ;
link . target = part . target . clone ( ) ;
}
}
" linkText " = > {
if let Some ( link ) = current_link . as_mut ( ) {
link . name = Some ( part . text . clone ( ) ) ;
}
}
_ = > out . push ( part . text . clone ( ) ) ,
}
}
replace_links ( out . join ( " " ) )
}
2020-12-07 05:46:39 -05:00
impl QuickInfo {
2022-03-23 09:54:22 -04:00
pub fn to_hover (
2022-02-09 18:08:53 -05:00
& self ,
line_index : Arc < LineIndex > ,
language_server : & language_server ::Inner ,
) -> lsp ::Hover {
let mut parts = Vec ::< lsp ::MarkedString > ::new ( ) ;
2021-03-15 18:01:41 -04:00
if let Some ( display_string ) = self
. display_parts
. clone ( )
2022-02-09 18:08:53 -05:00
. map ( | p | display_parts_to_string ( & p , language_server ) )
2020-12-07 05:46:39 -05:00
{
2022-02-09 18:08:53 -05:00
parts . push ( lsp ::MarkedString ::from_language_code (
2020-12-07 05:46:39 -05:00
" typescript " . to_string ( ) ,
display_string ,
) ) ;
}
2021-03-15 18:01:41 -04:00
if let Some ( documentation ) = self
. documentation
. clone ( )
2022-02-09 18:08:53 -05:00
. map ( | p | display_parts_to_string ( & p , language_server ) )
2020-12-07 05:46:39 -05:00
{
2022-02-09 18:08:53 -05:00
parts . push ( lsp ::MarkedString ::from_markdown ( documentation ) ) ;
2020-12-07 05:46:39 -05:00
}
if let Some ( tags ) = & self . tags {
let tags_preview = tags
. iter ( )
2022-02-09 18:08:53 -05:00
. map ( | tag_info | get_tag_documentation ( tag_info , language_server ) )
2020-12-07 05:46:39 -05:00
. collect ::< Vec < String > > ( )
. join ( " \n \n " ) ;
if ! tags_preview . is_empty ( ) {
2022-02-09 18:08:53 -05:00
parts . push ( lsp ::MarkedString ::from_markdown ( format! (
2023-01-27 10:43:16 -05:00
" \n \n {tags_preview} "
2020-12-07 05:46:39 -05:00
) ) ) ;
}
}
2021-01-29 14:34:33 -05:00
lsp ::Hover {
2022-02-09 18:08:53 -05:00
contents : lsp ::HoverContents ::Array ( parts ) ,
2020-12-07 05:46:39 -05:00
range : Some ( self . text_span . to_range ( line_index ) ) ,
}
}
}
2022-05-31 20:19:18 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize) ]
2021-01-12 16:53:27 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct DocumentSpan {
text_span : TextSpan ,
pub file_name : String ,
original_text_span : Option < TextSpan > ,
2022-02-23 16:01:20 -05:00
// original_file_name: Option<String>,
2021-01-12 16:53:27 -05:00
context_span : Option < TextSpan > ,
original_context_span : Option < TextSpan > ,
}
impl DocumentSpan {
2022-04-25 11:23:24 -04:00
pub fn to_link (
2021-01-12 16:53:27 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-02-06 07:39:01 -05:00
) -> Option < lsp ::LocationLink > {
2021-07-25 01:33:42 -04:00
let target_specifier = normalize_specifier ( & self . file_name ) . ok ( ) ? ;
2022-04-25 11:23:24 -04:00
let target_asset_or_doc =
language_server . get_maybe_asset_or_document ( & target_specifier ) ? ;
2021-11-12 11:42:04 -05:00
let target_line_index = target_asset_or_doc . line_index ( ) ;
2021-02-17 23:37:05 -05:00
let target_uri = language_server
. url_map
. normalize_specifier ( & target_specifier )
2021-07-25 01:33:42 -04:00
. ok ( ) ? ;
2021-02-17 23:37:05 -05:00
let ( target_range , target_selection_range ) =
if let Some ( context_span ) = & self . context_span {
(
2021-10-28 19:56:01 -04:00
context_span . to_range ( target_line_index . clone ( ) ) ,
self . text_span . to_range ( target_line_index ) ,
2021-02-17 23:37:05 -05:00
)
} else {
(
2021-10-28 19:56:01 -04:00
self . text_span . to_range ( target_line_index . clone ( ) ) ,
self . text_span . to_range ( target_line_index ) ,
2021-02-17 23:37:05 -05:00
)
2021-01-12 16:53:27 -05:00
} ;
2021-02-17 23:37:05 -05:00
let origin_selection_range =
if let Some ( original_context_span ) = & self . original_context_span {
Some ( original_context_span . to_range ( line_index ) )
} else {
2021-03-25 14:17:37 -04:00
self
. original_text_span
. as_ref ( )
. map ( | original_text_span | original_text_span . to_range ( line_index ) )
2021-02-17 23:37:05 -05:00
} ;
let link = lsp ::LocationLink {
origin_selection_range ,
2023-03-23 10:23:04 -04:00
target_uri : target_uri . into_url ( ) ,
2021-02-17 23:37:05 -05:00
target_range ,
target_selection_range ,
} ;
Some ( link )
2021-01-12 16:53:27 -05:00
}
2022-02-09 18:08:53 -05:00
/// Convert the `DocumentSpan` into a specifier that can be sent to the client
/// to link to the target document span. Used for converting JSDoc symbol
/// links to markdown links.
fn to_target (
& self ,
language_server : & language_server ::Inner ,
) -> Option < ModuleSpecifier > {
let specifier = normalize_specifier ( & self . file_name ) . ok ( ) ? ;
let asset_or_doc =
2022-04-25 11:23:24 -04:00
language_server . get_maybe_asset_or_document ( & specifier ) ? ;
2022-02-09 18:08:53 -05:00
let line_index = asset_or_doc . line_index ( ) ;
let range = self . text_span . to_range ( line_index ) ;
let mut target = language_server
. url_map
. normalize_specifier ( & specifier )
2023-03-23 10:23:04 -04:00
. ok ( ) ?
. into_url ( ) ;
2022-02-09 18:08:53 -05:00
target . set_fragment ( Some ( & format! (
" L{},{} " ,
range . start . line + 1 ,
range . start . character + 1
) ) ) ;
Some ( target )
}
2021-01-12 16:53:27 -05:00
}
2021-11-22 19:08:56 -05:00
#[ derive(Debug, Clone, Deserialize) ]
pub enum MatchKind {
#[ serde(rename = " exact " ) ]
Exact ,
#[ serde(rename = " prefix " ) ]
Prefix ,
#[ serde(rename = " substring " ) ]
Substring ,
#[ serde(rename = " camelCase " ) ]
CamelCase ,
}
#[ derive(Debug, Clone, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct NavigateToItem {
name : String ,
kind : ScriptElementKind ,
kind_modifiers : String ,
2022-02-23 16:01:20 -05:00
// match_kind: MatchKind,
// is_case_sensitive: bool,
2021-11-22 19:08:56 -05:00
file_name : String ,
text_span : TextSpan ,
container_name : Option < String > ,
2022-02-23 16:01:20 -05:00
// container_kind: ScriptElementKind,
2021-11-22 19:08:56 -05:00
}
impl NavigateToItem {
2022-04-25 11:23:24 -04:00
pub fn to_symbol_information (
2021-11-22 19:08:56 -05:00
& self ,
2022-12-19 20:22:17 -05:00
language_server : & language_server ::Inner ,
2021-11-22 19:08:56 -05:00
) -> Option < lsp ::SymbolInformation > {
let specifier = normalize_specifier ( & self . file_name ) . ok ( ) ? ;
2022-04-25 11:23:24 -04:00
let asset_or_doc =
language_server . get_asset_or_document ( & specifier ) . ok ( ) ? ;
2021-11-22 19:08:56 -05:00
let line_index = asset_or_doc . line_index ( ) ;
let uri = language_server
. url_map
. normalize_specifier ( & specifier )
. ok ( ) ? ;
let range = self . text_span . to_range ( line_index ) ;
2023-03-23 10:23:04 -04:00
let location = lsp ::Location {
uri : uri . into_url ( ) ,
range ,
} ;
2021-11-22 19:08:56 -05:00
let mut tags : Option < Vec < lsp ::SymbolTag > > = None ;
let kind_modifiers = parse_kind_modifier ( & self . kind_modifiers ) ;
if kind_modifiers . contains ( " deprecated " ) {
2021-11-24 20:10:12 -05:00
tags = Some ( vec! [ lsp ::SymbolTag ::DEPRECATED ] ) ;
2021-11-22 19:08:56 -05:00
}
// The field `deprecated` is deprecated but SymbolInformation does not have
// a default, therefore we have to supply the deprecated deprecated
// field. It is like a bad version of Inception.
#[ allow(deprecated) ]
Some ( lsp ::SymbolInformation {
name : self . name . clone ( ) ,
kind : self . kind . clone ( ) . into ( ) ,
tags ,
deprecated : None ,
location ,
container_name : self . container_name . clone ( ) ,
} )
}
}
2022-10-15 22:39:43 -04:00
#[ derive(Debug, Clone, Deserialize) ]
pub enum InlayHintKind {
Type ,
Parameter ,
Enum ,
}
impl InlayHintKind {
pub fn to_lsp ( & self ) -> Option < lsp ::InlayHintKind > {
match self {
Self ::Enum = > None ,
Self ::Parameter = > Some ( lsp ::InlayHintKind ::PARAMETER ) ,
Self ::Type = > Some ( lsp ::InlayHintKind ::TYPE ) ,
}
}
}
#[ derive(Debug, Clone, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHint {
pub text : String ,
pub position : u32 ,
pub kind : InlayHintKind ,
pub whitespace_before : Option < bool > ,
pub whitespace_after : Option < bool > ,
}
impl InlayHint {
pub fn to_lsp ( & self , line_index : Arc < LineIndex > ) -> lsp ::InlayHint {
lsp ::InlayHint {
position : line_index . position_tsc ( self . position . into ( ) ) ,
label : lsp ::InlayHintLabel ::String ( self . text . clone ( ) ) ,
kind : self . kind . to_lsp ( ) ,
padding_left : self . whitespace_before ,
padding_right : self . whitespace_after ,
text_edits : None ,
tooltip : None ,
data : None ,
}
}
}
2021-01-31 22:30:41 -05:00
#[ derive(Debug, Clone, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct NavigationTree {
pub text : String ,
pub kind : ScriptElementKind ,
pub kind_modifiers : String ,
pub spans : Vec < TextSpan > ,
pub name_span : Option < TextSpan > ,
pub child_items : Option < Vec < NavigationTree > > ,
}
impl NavigationTree {
pub fn to_code_lens (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-01-31 22:30:41 -05:00
specifier : & ModuleSpecifier ,
2021-06-04 17:31:44 -04:00
source : & code_lens ::CodeLensSource ,
2021-01-31 22:30:41 -05:00
) -> lsp ::CodeLens {
2021-02-08 05:45:10 -05:00
let range = if let Some ( name_span ) = & self . name_span {
name_span . to_range ( line_index )
} else if ! self . spans . is_empty ( ) {
let span = & self . spans [ 0 ] ;
span . to_range ( line_index )
} else {
lsp ::Range ::default ( )
} ;
2021-01-31 22:30:41 -05:00
lsp ::CodeLens {
2021-02-08 05:45:10 -05:00
range ,
2021-01-31 22:30:41 -05:00
command : None ,
data : Some ( json! ( {
" specifier " : specifier ,
" source " : source
} ) ) ,
}
}
2021-04-19 21:29:27 -04:00
pub fn collect_document_symbols (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-04-19 21:29:27 -04:00
document_symbols : & mut Vec < lsp ::DocumentSymbol > ,
) -> bool {
let mut should_include = self . should_include_entry ( ) ;
if ! should_include
2023-03-15 17:46:36 -04:00
& & self
. child_items
. as_ref ( )
. map ( | v | v . is_empty ( ) )
. unwrap_or ( true )
2021-04-19 21:29:27 -04:00
{
return false ;
}
let children = self
. child_items
2023-03-15 17:46:36 -04:00
. as_deref ( )
. unwrap_or ( & [ ] as & [ NavigationTree ] ) ;
2021-04-19 21:29:27 -04:00
for span in self . spans . iter ( ) {
let range = TextRange ::at ( span . start . into ( ) , span . length . into ( ) ) ;
let mut symbol_children = Vec ::< lsp ::DocumentSymbol > ::new ( ) ;
for child in children . iter ( ) {
let should_traverse_child = child
. spans
. iter ( )
. map ( | child_span | {
TextRange ::at ( child_span . start . into ( ) , child_span . length . into ( ) )
} )
. any ( | child_range | range . intersect ( child_range ) . is_some ( ) ) ;
if should_traverse_child {
2021-10-28 19:56:01 -04:00
let included_child = child
. collect_document_symbols ( line_index . clone ( ) , & mut symbol_children ) ;
2021-04-19 21:29:27 -04:00
should_include = should_include | | included_child ;
}
}
if should_include {
let mut selection_span = span ;
if let Some ( name_span ) = self . name_span . as_ref ( ) {
let name_range =
TextRange ::at ( name_span . start . into ( ) , name_span . length . into ( ) ) ;
if range . contains_range ( name_range ) {
selection_span = name_span ;
}
}
2021-11-22 19:08:56 -05:00
let name = match self . kind {
ScriptElementKind ::MemberGetAccessorElement = > {
format! ( " (get) {} " , self . text )
}
ScriptElementKind ::MemberSetAccessorElement = > {
format! ( " (set) {} " , self . text )
}
_ = > self . text . clone ( ) ,
} ;
2021-04-19 21:29:27 -04:00
let mut tags : Option < Vec < lsp ::SymbolTag > > = None ;
let kind_modifiers = parse_kind_modifier ( & self . kind_modifiers ) ;
if kind_modifiers . contains ( " deprecated " ) {
2021-11-24 20:10:12 -05:00
tags = Some ( vec! [ lsp ::SymbolTag ::DEPRECATED ] ) ;
2021-04-19 21:29:27 -04:00
}
let children = if ! symbol_children . is_empty ( ) {
Some ( symbol_children )
} else {
None
} ;
// The field `deprecated` is deprecated but DocumentSymbol does not have
// a default, therefore we have to supply the deprecated deprecated
// field. It is like a bad version of Inception.
#[ allow(deprecated) ]
document_symbols . push ( lsp ::DocumentSymbol {
2021-11-22 19:08:56 -05:00
name ,
2021-04-19 21:29:27 -04:00
kind : self . kind . clone ( ) . into ( ) ,
2021-10-28 19:56:01 -04:00
range : span . to_range ( line_index . clone ( ) ) ,
selection_range : selection_span . to_range ( line_index . clone ( ) ) ,
2021-04-19 21:29:27 -04:00
tags ,
children ,
detail : None ,
deprecated : None ,
} )
}
}
should_include
}
fn should_include_entry ( & self ) -> bool {
if let ScriptElementKind ::Alias = self . kind {
return false ;
}
! self . text . is_empty ( ) & & self . text ! = " <function> " & & self . text ! = " <class> "
}
2021-01-31 22:30:41 -05:00
pub fn walk < F > ( & self , callback : & F )
where
F : Fn ( & NavigationTree , Option < & NavigationTree > ) ,
{
callback ( self , None ) ;
if let Some ( child_items ) = & self . child_items {
for child in child_items {
child . walk_child ( callback , self ) ;
}
}
}
fn walk_child < F > ( & self , callback : & F , parent : & NavigationTree )
where
F : Fn ( & NavigationTree , Option < & NavigationTree > ) ,
{
callback ( self , Some ( parent ) ) ;
if let Some ( child_items ) = & self . child_items {
for child in child_items {
child . walk_child ( callback , self ) ;
}
}
}
}
2021-01-12 16:53:27 -05:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ImplementationLocation {
#[ serde(flatten) ]
pub document_span : DocumentSpan ,
// ImplementationLocation props
2022-02-23 16:01:20 -05:00
// kind: ScriptElementKind,
// display_parts: Vec<SymbolDisplayPart>,
2021-01-12 16:53:27 -05:00
}
2021-02-08 05:45:10 -05:00
impl ImplementationLocation {
2022-03-23 09:54:22 -04:00
pub fn to_location (
2021-02-17 23:37:05 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-02-17 23:37:05 -05:00
) -> lsp ::Location {
2021-07-25 01:33:42 -04:00
let specifier = normalize_specifier ( & self . document_span . file_name )
2023-02-06 16:49:49 -05:00
. unwrap_or_else ( | _ | ModuleSpecifier ::parse ( " deno://invalid " ) . unwrap ( ) ) ;
2021-02-17 23:37:05 -05:00
let uri = language_server
. url_map
. normalize_specifier ( & specifier )
2023-03-23 10:23:04 -04:00
. unwrap_or_else ( | _ | {
LspClientUrl ::new ( ModuleSpecifier ::parse ( " deno://invalid " ) . unwrap ( ) )
} ) ;
2021-02-08 05:45:10 -05:00
lsp ::Location {
2023-03-23 10:23:04 -04:00
uri : uri . into_url ( ) ,
2021-02-08 05:45:10 -05:00
range : self . document_span . text_span . to_range ( line_index ) ,
}
}
2021-02-17 22:15:13 -05:00
2022-04-25 11:23:24 -04:00
pub fn to_link (
2021-02-17 22:15:13 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-02-17 22:15:13 -05:00
) -> Option < lsp ::LocationLink > {
2022-04-25 11:23:24 -04:00
self . document_span . to_link ( line_index , language_server )
2021-02-17 22:15:13 -05:00
}
2021-02-08 05:45:10 -05:00
}
2020-12-29 19:58:20 -05:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct RenameLocation {
2021-01-16 07:00:42 -05:00
#[ serde(flatten) ]
document_span : DocumentSpan ,
2020-12-29 19:58:20 -05:00
// RenameLocation props
2022-02-23 16:01:20 -05:00
// prefix_text: Option<String>,
// suffix_text: Option<String>,
2020-12-29 19:58:20 -05:00
}
pub struct RenameLocations {
pub locations : Vec < RenameLocation > ,
}
impl RenameLocations {
2022-03-23 09:54:22 -04:00
pub async fn into_workspace_edit (
2020-12-29 19:58:20 -05:00
self ,
new_name : & str ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-02-06 07:39:01 -05:00
) -> Result < lsp ::WorkspaceEdit , AnyError > {
2023-03-23 10:23:04 -04:00
let mut text_document_edit_map : HashMap <
LspClientUrl ,
lsp ::TextDocumentEdit ,
> = HashMap ::new ( ) ;
2020-12-29 19:58:20 -05:00
for location in self . locations . iter ( ) {
2021-06-21 17:18:32 -04:00
let specifier = normalize_specifier ( & location . document_span . file_name ) ? ;
2021-02-17 23:37:05 -05:00
let uri = language_server . url_map . normalize_specifier ( & specifier ) ? ;
2022-04-25 11:23:24 -04:00
let asset_or_doc = language_server . get_asset_or_document ( & specifier ) ? ;
2020-12-29 19:58:20 -05:00
// ensure TextDocumentEdit for `location.file_name`.
if text_document_edit_map . get ( & uri ) . is_none ( ) {
text_document_edit_map . insert (
uri . clone ( ) ,
2021-01-29 14:34:33 -05:00
lsp ::TextDocumentEdit {
text_document : lsp ::OptionalVersionedTextDocumentIdentifier {
2023-03-23 10:23:04 -04:00
uri : uri . as_url ( ) . clone ( ) ,
2021-11-18 13:50:24 -05:00
version : asset_or_doc . document_lsp_version ( ) ,
2020-12-29 19:58:20 -05:00
} ,
2021-01-29 14:34:33 -05:00
edits :
Vec ::< lsp ::OneOf < lsp ::TextEdit , lsp ::AnnotatedTextEdit > > ::new ( ) ,
2020-12-29 19:58:20 -05:00
} ,
) ;
}
// push TextEdit for ensured `TextDocumentEdit.edits`.
let document_edit = text_document_edit_map . get_mut ( & uri ) . unwrap ( ) ;
2021-01-29 14:34:33 -05:00
document_edit . edits . push ( lsp ::OneOf ::Left ( lsp ::TextEdit {
range : location
. document_span
. text_span
2021-11-12 11:42:04 -05:00
. to_range ( asset_or_doc . line_index ( ) ) ,
2021-01-29 14:34:33 -05:00
new_text : new_name . to_string ( ) ,
} ) ) ;
2020-12-29 19:58:20 -05:00
}
2021-01-29 14:34:33 -05:00
Ok ( lsp ::WorkspaceEdit {
2021-01-12 02:50:02 -05:00
change_annotations : None ,
2020-12-29 19:58:20 -05:00
changes : None ,
2021-01-29 14:34:33 -05:00
document_changes : Some ( lsp ::DocumentChanges ::Edits (
2020-12-29 19:58:20 -05:00
text_document_edit_map . values ( ) . cloned ( ) . collect ( ) ,
) ) ,
} )
}
}
2020-12-07 05:46:39 -05:00
#[ derive(Debug, Deserialize) ]
pub enum HighlightSpanKind {
#[ serde(rename = " none " ) ]
None ,
#[ serde(rename = " definition " ) ]
Definition ,
#[ serde(rename = " reference " ) ]
Reference ,
#[ serde(rename = " writtenReference " ) ]
WrittenReference ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct HighlightSpan {
2022-02-23 16:01:20 -05:00
// file_name: Option<String>,
// is_in_string: Option<bool>,
2020-12-07 05:46:39 -05:00
text_span : TextSpan ,
2022-02-23 16:01:20 -05:00
// context_span: Option<TextSpan>,
2020-12-07 05:46:39 -05:00
kind : HighlightSpanKind ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct DefinitionInfo {
2022-02-23 16:01:20 -05:00
// kind: ScriptElementKind,
// name: String,
// container_kind: Option<ScriptElementKind>,
// container_name: Option<String>,
2021-01-12 16:53:27 -05:00
#[ serde(flatten) ]
pub document_span : DocumentSpan ,
2020-12-07 05:46:39 -05:00
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct DefinitionInfoAndBoundSpan {
pub definitions : Option < Vec < DefinitionInfo > > ,
2022-02-23 16:01:20 -05:00
// text_span: TextSpan,
2020-12-07 05:46:39 -05:00
}
impl DefinitionInfoAndBoundSpan {
2022-03-23 09:54:22 -04:00
pub async fn to_definition (
2020-12-07 05:46:39 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-02-06 07:39:01 -05:00
) -> Option < lsp ::GotoDefinitionResponse > {
2020-12-07 05:46:39 -05:00
if let Some ( definitions ) = & self . definitions {
2021-01-29 14:34:33 -05:00
let mut location_links = Vec ::< lsp ::LocationLink > ::new ( ) ;
2020-12-21 08:44:26 -05:00
for di in definitions {
2021-10-28 19:56:01 -04:00
if let Some ( link ) = di
. document_span
. to_link ( line_index . clone ( ) , language_server )
2021-01-12 16:53:27 -05:00
{
location_links . push ( link ) ;
2020-12-21 08:44:26 -05:00
}
}
2021-01-29 14:34:33 -05:00
Some ( lsp ::GotoDefinitionResponse ::Link ( location_links ) )
2020-12-07 05:46:39 -05:00
} else {
None
}
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct DocumentHighlights {
2022-02-23 16:01:20 -05:00
// file_name: String,
2020-12-07 05:46:39 -05:00
highlight_spans : Vec < HighlightSpan > ,
}
impl DocumentHighlights {
pub fn to_highlight (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-01-29 14:34:33 -05:00
) -> Vec < lsp ::DocumentHighlight > {
2020-12-07 05:46:39 -05:00
self
. highlight_spans
. iter ( )
2021-01-29 14:34:33 -05:00
. map ( | hs | lsp ::DocumentHighlight {
2021-10-28 19:56:01 -04:00
range : hs . text_span . to_range ( line_index . clone ( ) ) ,
2020-12-07 05:46:39 -05:00
kind : match hs . kind {
HighlightSpanKind ::WrittenReference = > {
2021-11-24 20:10:12 -05:00
Some ( lsp ::DocumentHighlightKind ::WRITE )
2020-12-07 05:46:39 -05:00
}
2021-11-24 20:10:12 -05:00
_ = > Some ( lsp ::DocumentHighlightKind ::READ ) ,
2020-12-07 05:46:39 -05:00
} ,
} )
. collect ( )
}
}
2021-02-04 13:53:02 -05:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct TextChange {
2021-05-29 07:21:11 -04:00
pub span : TextSpan ,
pub new_text : String ,
2021-02-04 13:53:02 -05:00
}
impl TextChange {
2022-07-11 19:35:18 -04:00
pub fn as_text_edit ( & self , line_index : Arc < LineIndex > ) -> lsp ::TextEdit {
lsp ::TextEdit {
range : self . span . to_range ( line_index ) ,
new_text : self . new_text . clone ( ) ,
}
}
pub fn as_text_or_annotated_text_edit (
2021-02-04 13:53:02 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-02-04 13:53:02 -05:00
) -> lsp ::OneOf < lsp ::TextEdit , lsp ::AnnotatedTextEdit > {
lsp ::OneOf ::Left ( lsp ::TextEdit {
range : self . span . to_range ( line_index ) ,
new_text : self . new_text . clone ( ) ,
} )
}
}
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct FileTextChanges {
2021-05-29 07:21:11 -04:00
pub file_name : String ,
pub text_changes : Vec < TextChange > ,
2021-02-04 13:53:02 -05:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-05-29 07:21:11 -04:00
pub is_new_file : Option < bool > ,
2021-02-04 13:53:02 -05:00
}
impl FileTextChanges {
2022-04-25 11:23:24 -04:00
pub fn to_text_document_edit (
2021-02-04 13:53:02 -05:00
& self ,
2022-01-18 16:28:47 -05:00
language_server : & language_server ::Inner ,
2021-02-06 07:39:01 -05:00
) -> Result < lsp ::TextDocumentEdit , AnyError > {
2021-06-21 17:18:32 -04:00
let specifier = normalize_specifier ( & self . file_name ) ? ;
2022-04-25 11:23:24 -04:00
let asset_or_doc = language_server . get_asset_or_document ( & specifier ) ? ;
2021-02-04 13:53:02 -05:00
let edits = self
. text_changes
. iter ( )
2022-07-11 19:35:18 -04:00
. map ( | tc | tc . as_text_or_annotated_text_edit ( asset_or_doc . line_index ( ) ) )
2021-02-04 13:53:02 -05:00
. collect ( ) ;
Ok ( lsp ::TextDocumentEdit {
text_document : lsp ::OptionalVersionedTextDocumentIdentifier {
2022-04-25 11:23:24 -04:00
uri : specifier ,
2021-11-18 13:50:24 -05:00
version : asset_or_doc . document_lsp_version ( ) ,
2021-02-04 13:53:02 -05:00
} ,
edits ,
} )
}
2021-08-05 21:46:32 -04:00
2022-04-25 11:23:24 -04:00
pub fn to_text_document_change_ops (
2021-08-05 21:46:32 -04:00
& self ,
2022-01-18 16:28:47 -05:00
language_server : & language_server ::Inner ,
2021-08-05 21:46:32 -04:00
) -> Result < Vec < lsp ::DocumentChangeOperation > , AnyError > {
let mut ops = Vec ::< lsp ::DocumentChangeOperation > ::new ( ) ;
let specifier = normalize_specifier ( & self . file_name ) ? ;
2021-11-12 11:42:04 -05:00
let maybe_asset_or_document = if ! self . is_new_file . unwrap_or ( false ) {
2022-04-25 11:23:24 -04:00
let asset_or_doc = language_server . get_asset_or_document ( & specifier ) ? ;
2021-11-12 11:42:04 -05:00
Some ( asset_or_doc )
2021-08-05 21:46:32 -04:00
} else {
2021-11-12 11:42:04 -05:00
None
2021-08-05 21:46:32 -04:00
} ;
2021-11-12 11:42:04 -05:00
let line_index = maybe_asset_or_document
. as_ref ( )
. map ( | d | d . line_index ( ) )
. unwrap_or_else ( | | Arc ::new ( LineIndex ::new ( " " ) ) ) ;
2021-08-05 21:46:32 -04:00
if self . is_new_file . unwrap_or ( false ) {
ops . push ( lsp ::DocumentChangeOperation ::Op ( lsp ::ResourceOp ::Create (
lsp ::CreateFile {
uri : specifier . clone ( ) ,
options : Some ( lsp ::CreateFileOptions {
ignore_if_exists : Some ( true ) ,
overwrite : None ,
} ) ,
annotation_id : None ,
} ,
) ) ) ;
}
let edits = self
. text_changes
. iter ( )
2022-07-11 19:35:18 -04:00
. map ( | tc | tc . as_text_or_annotated_text_edit ( line_index . clone ( ) ) )
2021-08-05 21:46:32 -04:00
. collect ( ) ;
ops . push ( lsp ::DocumentChangeOperation ::Edit ( lsp ::TextDocumentEdit {
text_document : lsp ::OptionalVersionedTextDocumentIdentifier {
2022-04-25 11:23:24 -04:00
uri : specifier ,
2022-02-24 20:03:12 -05:00
version : maybe_asset_or_document . and_then ( | d | d . document_lsp_version ( ) ) ,
2021-08-05 21:46:32 -04:00
} ,
edits ,
} ) ) ;
Ok ( ops )
}
2021-02-04 13:53:02 -05:00
}
2021-04-19 21:26:36 -04:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct Classifications {
spans : Vec < u32 > ,
}
impl Classifications {
pub fn to_semantic_tokens (
& self ,
2022-03-02 16:06:38 -05:00
asset_or_doc : & AssetOrDocument ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-11-16 17:23:25 -05:00
) -> LspResult < lsp ::SemanticTokens > {
2021-04-19 21:26:36 -04:00
let token_count = self . spans . len ( ) / 3 ;
let mut builder = SemanticTokensBuilder ::new ( ) ;
for i in 0 .. token_count {
let src_offset = 3 * i ;
let offset = self . spans [ src_offset ] ;
let length = self . spans [ src_offset + 1 ] ;
let ts_classification = self . spans [ src_offset + 2 ] ;
let token_type =
Classifications ::get_token_type_from_classification ( ts_classification ) ;
let token_modifiers =
Classifications ::get_token_modifier_from_classification (
ts_classification ,
) ;
let start_pos = line_index . position_tsc ( offset . into ( ) ) ;
let end_pos = line_index . position_tsc ( TextSize ::from ( offset + length ) ) ;
2021-11-16 17:23:25 -05:00
if start_pos . line = = end_pos . line
& & start_pos . character < = end_pos . character
{
builder . push (
start_pos . line ,
start_pos . character ,
end_pos . character - start_pos . character ,
token_type ,
token_modifiers ,
) ;
} else {
log ::error! (
2022-03-02 16:06:38 -05:00
" unexpected positions \n specifier: {} \n open: {} \n start_pos: {:?} \n end_pos: {:?} " ,
asset_or_doc . specifier ( ) ,
asset_or_doc . is_open ( ) ,
2021-11-16 17:23:25 -05:00
start_pos ,
end_pos
) ;
return Err ( LspError ::internal_error ( ) ) ;
}
2021-04-19 21:26:36 -04:00
}
2021-11-16 17:23:25 -05:00
Ok ( builder . build ( None ) )
2021-04-19 21:26:36 -04:00
}
fn get_token_type_from_classification ( ts_classification : u32 ) -> u32 {
2021-11-22 19:08:56 -05:00
assert! ( ts_classification > semantic_tokens ::MODIFIER_MASK ) ;
( ts_classification > > semantic_tokens ::TYPE_OFFSET ) - 1
2021-04-19 21:26:36 -04:00
}
fn get_token_modifier_from_classification ( ts_classification : u32 ) -> u32 {
2021-11-22 19:08:56 -05:00
ts_classification & semantic_tokens ::MODIFIER_MASK
2021-04-19 21:26:36 -04:00
}
}
2021-08-05 21:46:32 -04:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct RefactorActionInfo {
name : String ,
description : String ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
not_applicable_reason : Option < String > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
kind : Option < String > ,
}
impl RefactorActionInfo {
pub fn get_action_kind ( & self ) -> lsp ::CodeActionKind {
if let Some ( kind ) = & self . kind {
kind . clone ( ) . into ( )
} else {
let maybe_match = ALL_KNOWN_REFACTOR_ACTION_KINDS
. iter ( )
. find ( | action | action . matches ( & self . name ) ) ;
maybe_match
2023-03-15 17:46:36 -04:00
. map ( | action | action . kind . clone ( ) )
. unwrap_or ( lsp ::CodeActionKind ::REFACTOR )
2021-08-05 21:46:32 -04:00
}
}
pub fn is_preferred ( & self , all_actions : & [ RefactorActionInfo ] ) -> bool {
if EXTRACT_CONSTANT . matches ( & self . name ) {
let get_scope = | name : & str | -> Option < u32 > {
2021-09-15 22:07:52 -04:00
if let Some ( captures ) = SCOPE_RE . captures ( name ) {
2021-08-05 21:46:32 -04:00
captures [ 1 ] . parse ::< u32 > ( ) . ok ( )
} else {
None
}
} ;
return if let Some ( scope ) = get_scope ( & self . name ) {
all_actions
. iter ( )
. filter ( | other | {
! std ::ptr ::eq ( & self , other ) & & EXTRACT_CONSTANT . matches ( & other . name )
} )
. all ( | other | {
if let Some ( other_scope ) = get_scope ( & other . name ) {
scope < other_scope
} else {
true
}
} )
} else {
false
} ;
}
if EXTRACT_TYPE . matches ( & self . name ) | | EXTRACT_INTERFACE . matches ( & self . name )
{
return true ;
}
false
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ApplicableRefactorInfo {
name : String ,
2022-02-23 16:01:20 -05:00
// description: String,
// #[serde(skip_serializing_if = "Option::is_none")]
// inlineable: Option<bool>,
2021-08-05 21:46:32 -04:00
actions : Vec < RefactorActionInfo > ,
}
impl ApplicableRefactorInfo {
pub fn to_code_actions (
& self ,
specifier : & ModuleSpecifier ,
range : & lsp ::Range ,
) -> Vec < lsp ::CodeAction > {
let mut code_actions = Vec ::< lsp ::CodeAction > ::new ( ) ;
// All typescript refactoring actions are inlineable
for action in self . actions . iter ( ) {
code_actions
. push ( self . as_inline_code_action ( action , specifier , range , & self . name ) ) ;
}
code_actions
}
fn as_inline_code_action (
& self ,
action : & RefactorActionInfo ,
specifier : & ModuleSpecifier ,
range : & lsp ::Range ,
refactor_name : & str ,
) -> lsp ::CodeAction {
let disabled = action . not_applicable_reason . as_ref ( ) . map ( | reason | {
lsp ::CodeActionDisabled {
reason : reason . clone ( ) ,
}
} ) ;
lsp ::CodeAction {
title : action . description . to_string ( ) ,
kind : Some ( action . get_action_kind ( ) ) ,
is_preferred : Some ( action . is_preferred ( & self . actions ) ) ,
disabled ,
data : Some (
serde_json ::to_value ( RefactorCodeActionData {
specifier : specifier . clone ( ) ,
range : * range ,
refactor_name : refactor_name . to_owned ( ) ,
action_name : action . name . clone ( ) ,
} )
. unwrap ( ) ,
) ,
.. Default ::default ( )
}
}
}
2023-08-25 20:50:47 -04:00
pub fn file_text_changes_to_workspace_edit (
changes : & [ FileTextChanges ] ,
language_server : & language_server ::Inner ,
) -> LspResult < Option < lsp ::WorkspaceEdit > > {
let mut all_ops = Vec ::< lsp ::DocumentChangeOperation > ::new ( ) ;
for change in changes {
let ops = match change . to_text_document_change_ops ( language_server ) {
Ok ( op ) = > op ,
Err ( err ) = > {
error! ( " Unable to convert changes to edits: {} " , err ) ;
return Err ( LspError ::internal_error ( ) ) ;
}
} ;
all_ops . extend ( ops ) ;
}
Ok ( Some ( lsp ::WorkspaceEdit {
document_changes : Some ( lsp ::DocumentChanges ::Operations ( all_ops ) ) ,
.. Default ::default ( )
} ) )
}
2021-08-05 21:46:32 -04:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct RefactorEditInfo {
edits : Vec < FileTextChanges > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub rename_location : Option < u32 > ,
}
impl RefactorEditInfo {
2022-03-23 09:54:22 -04:00
pub async fn to_workspace_edit (
2021-08-05 21:46:32 -04:00
& self ,
2022-01-18 16:28:47 -05:00
language_server : & language_server ::Inner ,
2023-08-25 20:50:47 -04:00
) -> LspResult < Option < lsp ::WorkspaceEdit > > {
file_text_changes_to_workspace_edit ( & self . edits , language_server )
2021-08-05 21:46:32 -04:00
}
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
2021-03-15 18:01:41 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct CodeAction {
2022-07-11 19:35:18 -04:00
description : String ,
changes : Vec < FileTextChanges > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
commands : Option < Vec < Value > > ,
2021-03-15 18:01:41 -04:00
}
2021-02-04 13:53:02 -05:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CodeFixAction {
pub description : String ,
pub changes : Vec < FileTextChanges > ,
// These are opaque types that should just be passed back when applying the
// action.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub commands : Option < Vec < Value > > ,
pub fix_name : String ,
// It appears currently that all fixIds are strings, but the protocol
// specifies an opaque type, the problem is that we need to use the id as a
// hash key, and `Value` does not implement hash (and it could provide a false
// positive depending on JSON whitespace, so we deserialize it but it might
// break in the future)
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub fix_id : Option < String > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub fix_all_description : Option < String > ,
}
#[ derive(Debug, Clone, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CombinedCodeActions {
pub changes : Vec < FileTextChanges > ,
pub commands : Option < Vec < Value > > ,
}
2020-12-07 05:46:39 -05:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
2023-03-30 12:15:21 -04:00
pub struct ReferencedSymbol {
pub definition : ReferencedSymbolDefinitionInfo ,
pub references : Vec < ReferencedSymbolEntry > ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ReferencedSymbolDefinitionInfo {
#[ serde(flatten) ]
pub definition_info : DefinitionInfo ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ReferencedSymbolEntry {
2022-05-31 20:19:18 -04:00
#[ serde(default) ]
2020-12-07 05:46:39 -05:00
pub is_definition : bool ,
2023-03-30 12:15:21 -04:00
#[ serde(flatten) ]
pub entry : ReferenceEntry ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ReferenceEntry {
// is_write_access: bool,
2022-02-23 16:01:20 -05:00
// is_in_string: Option<bool>,
2021-01-16 07:00:42 -05:00
#[ serde(flatten) ]
pub document_span : DocumentSpan ,
2020-12-07 05:46:39 -05:00
}
impl ReferenceEntry {
2022-03-23 09:54:22 -04:00
pub fn to_location (
2021-02-17 23:37:05 -05:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 17:10:14 -05:00
url_map : & LspUrlMap ,
2021-02-17 23:37:05 -05:00
) -> lsp ::Location {
2021-07-25 01:33:42 -04:00
let specifier = normalize_specifier ( & self . document_span . file_name )
. unwrap_or_else ( | _ | INVALID_SPECIFIER . clone ( ) ) ;
2022-01-19 17:10:14 -05:00
let uri = url_map
2021-02-17 23:37:05 -05:00
. normalize_specifier ( & specifier )
2023-03-23 10:23:04 -04:00
. unwrap_or_else ( | _ | LspClientUrl ::new ( INVALID_SPECIFIER . clone ( ) ) ) ;
2021-01-29 14:34:33 -05:00
lsp ::Location {
2023-03-23 10:23:04 -04:00
uri : uri . into_url ( ) ,
2021-01-16 07:00:42 -05:00
range : self . document_span . text_span . to_range ( line_index ) ,
2020-12-07 05:46:39 -05:00
}
}
}
2021-04-19 01:11:26 -04:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CallHierarchyItem {
name : String ,
kind : ScriptElementKind ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
kind_modifiers : Option < String > ,
file : String ,
span : TextSpan ,
selection_span : TextSpan ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
container_name : Option < String > ,
}
impl CallHierarchyItem {
2022-04-25 11:23:24 -04:00
pub fn try_resolve_call_hierarchy_item (
2021-04-19 01:11:26 -04:00
& self ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-04-19 01:11:26 -04:00
maybe_root_path : Option < & Path > ,
) -> Option < lsp ::CallHierarchyItem > {
2021-07-25 01:33:42 -04:00
let target_specifier = normalize_specifier ( & self . file ) . ok ( ) ? ;
2022-04-25 11:23:24 -04:00
let target_asset_or_doc =
language_server . get_maybe_asset_or_document ( & target_specifier ) ? ;
2021-04-19 01:11:26 -04:00
Some ( self . to_call_hierarchy_item (
2021-11-12 11:42:04 -05:00
target_asset_or_doc . line_index ( ) ,
2021-04-19 01:11:26 -04:00
language_server ,
maybe_root_path ,
) )
}
2022-03-23 09:54:22 -04:00
pub fn to_call_hierarchy_item (
2021-04-19 01:11:26 -04:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-04-19 01:11:26 -04:00
maybe_root_path : Option < & Path > ,
) -> lsp ::CallHierarchyItem {
2021-07-25 01:33:42 -04:00
let target_specifier = normalize_specifier ( & self . file )
. unwrap_or_else ( | _ | INVALID_SPECIFIER . clone ( ) ) ;
2021-04-19 01:11:26 -04:00
let uri = language_server
. url_map
. normalize_specifier ( & target_specifier )
2023-03-23 10:23:04 -04:00
. unwrap_or_else ( | _ | LspClientUrl ::new ( INVALID_SPECIFIER . clone ( ) ) ) ;
2021-04-19 01:11:26 -04:00
let use_file_name = self . is_source_file_item ( ) ;
2023-03-23 10:23:04 -04:00
let maybe_file_path = if uri . as_url ( ) . scheme ( ) = = " file " {
specifier_to_file_path ( uri . as_url ( ) ) . ok ( )
2021-04-19 01:11:26 -04:00
} else {
None
} ;
let name = if use_file_name {
if let Some ( file_path ) = maybe_file_path . as_ref ( ) {
file_path . file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( )
} else {
2023-03-23 10:23:04 -04:00
uri . as_str ( ) . to_string ( )
2021-04-19 01:11:26 -04:00
}
} else {
self . name . clone ( )
} ;
let detail = if use_file_name {
if let Some ( file_path ) = maybe_file_path . as_ref ( ) {
// TODO: update this to work with multi root workspaces
let parent_dir = file_path . parent ( ) . unwrap ( ) ;
if let Some ( root_path ) = maybe_root_path {
parent_dir
. strip_prefix ( root_path )
. unwrap_or ( parent_dir )
. to_string_lossy ( )
. to_string ( )
} else {
parent_dir . to_string_lossy ( ) . to_string ( )
}
} else {
String ::new ( )
}
} else {
self . container_name . as_ref ( ) . cloned ( ) . unwrap_or_default ( )
} ;
let mut tags : Option < Vec < lsp ::SymbolTag > > = None ;
if let Some ( modifiers ) = self . kind_modifiers . as_ref ( ) {
let kind_modifiers = parse_kind_modifier ( modifiers ) ;
if kind_modifiers . contains ( " deprecated " ) {
2021-11-24 20:10:12 -05:00
tags = Some ( vec! [ lsp ::SymbolTag ::DEPRECATED ] ) ;
2021-04-19 01:11:26 -04:00
}
}
lsp ::CallHierarchyItem {
name ,
tags ,
2023-03-23 10:23:04 -04:00
uri : uri . into_url ( ) ,
2021-04-19 01:11:26 -04:00
detail : Some ( detail ) ,
kind : self . kind . clone ( ) . into ( ) ,
2021-10-28 19:56:01 -04:00
range : self . span . to_range ( line_index . clone ( ) ) ,
2021-04-19 01:11:26 -04:00
selection_range : self . selection_span . to_range ( line_index ) ,
data : None ,
}
}
fn is_source_file_item ( & self ) -> bool {
self . kind = = ScriptElementKind ::ScriptElement
| | self . kind = = ScriptElementKind ::ModuleElement
& & self . selection_span . start = = 0
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CallHierarchyIncomingCall {
from : CallHierarchyItem ,
from_spans : Vec < TextSpan > ,
}
impl CallHierarchyIncomingCall {
2022-04-25 11:23:24 -04:00
pub fn try_resolve_call_hierarchy_incoming_call (
2021-04-19 01:11:26 -04:00
& self ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-04-19 01:11:26 -04:00
maybe_root_path : Option < & Path > ,
) -> Option < lsp ::CallHierarchyIncomingCall > {
2021-07-25 01:33:42 -04:00
let target_specifier = normalize_specifier ( & self . from . file ) . ok ( ) ? ;
2022-04-25 11:23:24 -04:00
let target_asset_or_doc =
language_server . get_maybe_asset_or_document ( & target_specifier ) ? ;
2021-04-19 01:11:26 -04:00
Some ( lsp ::CallHierarchyIncomingCall {
from : self . from . to_call_hierarchy_item (
2021-11-12 11:42:04 -05:00
target_asset_or_doc . line_index ( ) ,
2021-04-19 01:11:26 -04:00
language_server ,
maybe_root_path ,
) ,
from_ranges : self
. from_spans
. iter ( )
2021-11-12 11:42:04 -05:00
. map ( | span | span . to_range ( target_asset_or_doc . line_index ( ) ) )
2021-04-19 01:11:26 -04:00
. collect ( ) ,
} )
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CallHierarchyOutgoingCall {
to : CallHierarchyItem ,
from_spans : Vec < TextSpan > ,
}
impl CallHierarchyOutgoingCall {
2022-04-25 11:23:24 -04:00
pub fn try_resolve_call_hierarchy_outgoing_call (
2021-04-19 01:11:26 -04:00
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2022-01-19 11:38:40 -05:00
language_server : & language_server ::Inner ,
2021-04-19 01:11:26 -04:00
maybe_root_path : Option < & Path > ,
) -> Option < lsp ::CallHierarchyOutgoingCall > {
2021-07-25 01:33:42 -04:00
let target_specifier = normalize_specifier ( & self . to . file ) . ok ( ) ? ;
2022-04-25 11:23:24 -04:00
let target_asset_or_doc =
language_server . get_maybe_asset_or_document ( & target_specifier ) ? ;
2021-04-19 01:11:26 -04:00
Some ( lsp ::CallHierarchyOutgoingCall {
to : self . to . to_call_hierarchy_item (
2021-11-12 11:42:04 -05:00
target_asset_or_doc . line_index ( ) ,
2021-04-19 01:11:26 -04:00
language_server ,
maybe_root_path ,
) ,
from_ranges : self
. from_spans
. iter ( )
2021-10-28 19:56:01 -04:00
. map ( | span | span . to_range ( line_index . clone ( ) ) )
2021-04-19 01:11:26 -04:00
. collect ( ) ,
} )
}
}
2022-07-11 19:35:18 -04:00
/// Used to convert completion code actions into a command and additional text
/// edits to pass in the completion item.
fn parse_code_actions (
maybe_code_actions : Option < & Vec < CodeAction > > ,
data : & CompletionItemData ,
specifier : & ModuleSpecifier ,
language_server : & language_server ::Inner ,
) -> Result < ( Option < lsp ::Command > , Option < Vec < lsp ::TextEdit > > ) , AnyError > {
if let Some ( code_actions ) = maybe_code_actions {
let mut additional_text_edits : Vec < lsp ::TextEdit > = Vec ::new ( ) ;
let mut has_remaining_commands_or_edits = false ;
for ts_action in code_actions {
if ts_action . commands . is_some ( ) {
has_remaining_commands_or_edits = true ;
}
let asset_or_doc =
language_server . get_asset_or_document ( & data . specifier ) ? ;
for change in & ts_action . changes {
let change_specifier = normalize_specifier ( & change . file_name ) ? ;
if data . specifier = = change_specifier {
additional_text_edits . extend ( change . text_changes . iter ( ) . map ( | tc | {
2023-09-18 19:59:26 -04:00
let mut text_edit = tc . as_text_edit ( asset_or_doc . line_index ( ) ) ;
if let Some ( specifier_rewrite ) = & data . specifier_rewrite {
text_edit . new_text = text_edit
. new_text
. replace ( & specifier_rewrite . 0 , & specifier_rewrite . 1 ) ;
}
text_edit
2022-07-11 19:35:18 -04:00
} ) ) ;
} else {
has_remaining_commands_or_edits = true ;
}
}
}
let mut command : Option < lsp ::Command > = None ;
if has_remaining_commands_or_edits {
let actions : Vec < Value > = code_actions
. iter ( )
. map ( | ca | {
let changes : Vec < FileTextChanges > = ca
. changes
. clone ( )
. into_iter ( )
. filter ( | ch | {
normalize_specifier ( & ch . file_name ) . unwrap ( ) = = data . specifier
} )
. collect ( ) ;
json! ( {
" commands " : ca . commands ,
" description " : ca . description ,
" changes " : changes ,
} )
} )
. collect ( ) ;
command = Some ( lsp ::Command {
title : " " . to_string ( ) ,
command : " _typescript.applyCompletionCodeAction " . to_string ( ) ,
arguments : Some ( vec! [ json! ( specifier . to_string ( ) ) , json! ( actions ) ] ) ,
} ) ;
}
if additional_text_edits . is_empty ( ) {
Ok ( ( command , None ) )
} else {
Ok ( ( command , Some ( additional_text_edits ) ) )
}
} else {
Ok ( ( None , None ) )
}
}
2023-08-25 20:53:44 -04:00
// Based on https://github.com/microsoft/vscode/blob/1.81.1/extensions/typescript-language-features/src/languageFeatures/util/snippetForFunctionCall.ts#L49.
fn get_parameters_from_parts ( parts : & [ SymbolDisplayPart ] ) -> Vec < String > {
let mut parameters = Vec ::with_capacity ( 3 ) ;
let mut is_in_fn = false ;
let mut paren_count = 0 ;
let mut brace_count = 0 ;
for ( idx , part ) in parts . iter ( ) . enumerate ( ) {
if [ " methodName " , " functionName " , " text " , " propertyName " ]
. contains ( & part . kind . as_str ( ) )
{
if paren_count = = 0 & & brace_count = = 0 {
is_in_fn = true ;
}
} else if part . kind = = " parameterName " {
if paren_count = = 1 & & brace_count = = 0 & & is_in_fn {
let is_optional =
matches! ( parts . get ( idx + 1 ) , Some ( next ) if next . text = = " ? " ) ;
// Skip `this` and optional parameters.
if ! is_optional & & part . text ! = " this " {
parameters . push ( part . text . clone ( ) ) ;
}
}
} else if part . kind = = " punctuation " {
if part . text = = " ( " {
paren_count + = 1 ;
} else if part . text = = " ) " {
paren_count - = 1 ;
if paren_count < = 0 & & is_in_fn {
break ;
}
} else if part . text = = " ... " & & paren_count = = 1 {
// Found rest parmeter. Do not fill in any further arguments.
break ;
} else if part . text = = " { " {
brace_count + = 1 ;
} else if part . text = = " } " {
brace_count - = 1 ;
}
}
}
parameters
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
2020-12-08 05:36:13 -05:00
#[ serde(rename_all = " camelCase " ) ]
2021-03-15 18:01:41 -04:00
pub struct CompletionEntryDetails {
display_parts : Vec < SymbolDisplayPart > ,
documentation : Option < Vec < SymbolDisplayPart > > ,
2021-03-25 14:17:37 -04:00
tags : Option < Vec < JsDocTagInfo > > ,
2022-07-11 19:35:18 -04:00
name : String ,
kind : ScriptElementKind ,
kind_modifiers : String ,
code_actions : Option < Vec < CodeAction > > ,
source_display : Option < Vec < SymbolDisplayPart > > ,
2021-03-15 18:01:41 -04:00
}
impl CompletionEntryDetails {
2022-03-23 09:54:22 -04:00
pub fn as_completion_item (
2021-03-15 18:01:41 -04:00
& self ,
original_item : & lsp ::CompletionItem ,
2022-07-11 19:35:18 -04:00
data : & CompletionItemData ,
specifier : & ModuleSpecifier ,
2022-02-09 18:08:53 -05:00
language_server : & language_server ::Inner ,
2022-07-11 19:35:18 -04:00
) -> Result < lsp ::CompletionItem , AnyError > {
2021-03-15 18:01:41 -04:00
let detail = if original_item . detail . is_some ( ) {
original_item . detail . clone ( )
} else if ! self . display_parts . is_empty ( ) {
2022-12-17 17:20:15 -05:00
Some ( replace_links ( display_parts_to_string (
2022-02-09 18:08:53 -05:00
& self . display_parts ,
language_server ,
) ) )
2021-03-15 18:01:41 -04:00
} else {
None
} ;
let documentation = if let Some ( parts ) = & self . documentation {
2022-02-09 18:08:53 -05:00
let mut value = display_parts_to_string ( parts , language_server ) ;
2021-03-15 18:01:41 -04:00
if let Some ( tags ) = & self . tags {
let tag_documentation = tags
. iter ( )
2022-02-09 18:08:53 -05:00
. map ( | tag_info | get_tag_documentation ( tag_info , language_server ) )
2021-03-15 18:01:41 -04:00
. collect ::< Vec < String > > ( )
. join ( " " ) ;
2023-01-27 10:43:16 -05:00
value = format! ( " {value} \n \n {tag_documentation} " ) ;
2021-03-15 18:01:41 -04:00
}
Some ( lsp ::Documentation ::MarkupContent ( lsp ::MarkupContent {
kind : lsp ::MarkupKind ::Markdown ,
value ,
} ) )
} else {
None
} ;
2022-07-11 19:35:18 -04:00
let ( command , additional_text_edits ) = parse_code_actions (
self . code_actions . as_ref ( ) ,
data ,
specifier ,
language_server ,
) ? ;
2023-08-25 20:53:44 -04:00
let insert_text = if data . use_code_snippet {
Some ( format! (
" {}({}) " ,
original_item
. insert_text
. as_ref ( )
. unwrap_or ( & original_item . label ) ,
get_parameters_from_parts ( & self . display_parts ) . join ( " , " ) ,
) )
} else {
original_item . insert_text . clone ( )
} ;
2021-03-15 18:01:41 -04:00
2022-07-11 19:35:18 -04:00
Ok ( lsp ::CompletionItem {
2021-03-15 18:01:41 -04:00
data : None ,
detail ,
documentation ,
2022-07-11 19:35:18 -04:00
command ,
additional_text_edits ,
2023-08-25 20:53:44 -04:00
insert_text ,
2022-12-30 08:11:50 -05:00
// NOTE(bartlomieju): it's not entirely clear to me why we need to do that,
// but when `completionItem/resolve` is called, we get a list of commit chars
// even though we might have returned an empty list in `completion` request.
commit_characters : None ,
2021-03-15 18:01:41 -04:00
.. original_item . clone ( )
2022-07-11 19:35:18 -04:00
} )
2021-03-15 18:01:41 -04:00
}
}
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
2020-12-08 05:36:13 -05:00
pub struct CompletionInfo {
entries : Vec < CompletionEntry > ,
2022-07-24 17:11:33 -04:00
// this is only used by Microsoft's telemetrics, which Deno doesn't use and
// there are issues with the value not matching the type definitions.
// flags: Option<CompletionInfoFlags>,
2021-03-15 18:01:41 -04:00
is_global_completion : bool ,
2020-12-08 05:36:13 -05:00
is_member_completion : bool ,
2021-03-15 18:01:41 -04:00
is_new_identifier_location : bool ,
metadata : Option < Value > ,
optional_replacement_span : Option < TextSpan > ,
2020-12-08 05:36:13 -05:00
}
impl CompletionInfo {
2021-03-15 18:01:41 -04:00
pub fn as_completion_response (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-03-15 18:01:41 -04:00
settings : & config ::CompletionSettings ,
specifier : & ModuleSpecifier ,
position : u32 ,
2023-09-16 10:51:35 -04:00
language_server : & language_server ::Inner ,
2021-01-29 14:34:33 -05:00
) -> lsp ::CompletionResponse {
2020-12-08 05:36:13 -05:00
let items = self
. entries
2021-03-15 18:01:41 -04:00
. iter ( )
. map ( | entry | {
2021-10-28 19:56:01 -04:00
entry . as_completion_item (
line_index . clone ( ) ,
self ,
settings ,
specifier ,
position ,
2023-09-16 10:51:35 -04:00
language_server ,
2021-10-28 19:56:01 -04:00
)
2021-03-15 18:01:41 -04:00
} )
2020-12-08 05:36:13 -05:00
. collect ( ) ;
2021-03-15 18:01:41 -04:00
let is_incomplete = self
. metadata
. clone ( )
. map ( | v | {
v . as_object ( )
. unwrap ( )
. get ( " isIncomplete " )
. unwrap_or ( & json! ( false ) )
. as_bool ( )
. unwrap ( )
} )
. unwrap_or ( false ) ;
lsp ::CompletionResponse ::List ( lsp ::CompletionList {
is_incomplete ,
items ,
} )
2020-12-08 05:36:13 -05:00
}
}
2021-03-15 18:01:41 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct CompletionItemData {
pub specifier : ModuleSpecifier ,
pub position : u32 ,
pub name : String ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub source : Option < String > ,
2023-09-18 19:59:26 -04:00
/// If present, the code action / text edit corresponding to this item should
/// be rewritten by replacing the first string with the second. Intended for
/// auto-import specifiers to be reverse-import-mapped.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub specifier_rewrite : Option < ( String , String ) > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub data : Option < Value > ,
pub use_code_snippet : bool ,
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct CompletionEntryDataImport {
module_specifier : String ,
file_name : String ,
}
2021-03-15 18:01:41 -04:00
#[ derive(Debug, Default, Deserialize, Serialize) ]
2020-12-08 05:36:13 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct CompletionEntry {
2021-03-15 18:01:41 -04:00
name : String ,
2020-12-08 05:36:13 -05:00
kind : ScriptElementKind ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
kind_modifiers : Option < String > ,
sort_text : String ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
insert_text : Option < String > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-05-31 20:19:18 -04:00
is_snippet : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
replacement_span : Option < TextSpan > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
has_action : Option < bool > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
source : Option < String > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-05-31 20:19:18 -04:00
source_display : Option < Vec < SymbolDisplayPart > > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
label_details : Option < CompletionEntryLabelDetails > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
is_recommended : Option < bool > ,
2021-03-15 18:01:41 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
is_from_unchecked_file : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-05-31 20:19:18 -04:00
is_package_json_import : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
is_import_statement_completion : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-03-15 18:01:41 -04:00
data : Option < Value > ,
2020-12-08 05:36:13 -05:00
}
impl CompletionEntry {
2021-03-15 18:01:41 -04:00
fn get_commit_characters (
& self ,
info : & CompletionInfo ,
settings : & config ::CompletionSettings ,
) -> Option < Vec < String > > {
if info . is_new_identifier_location {
return None ;
}
2020-12-08 05:36:13 -05:00
2021-03-15 18:01:41 -04:00
let mut commit_characters = vec! [ ] ;
match self . kind {
ScriptElementKind ::MemberGetAccessorElement
| ScriptElementKind ::MemberSetAccessorElement
| ScriptElementKind ::ConstructSignatureElement
| ScriptElementKind ::CallSignatureElement
| ScriptElementKind ::IndexSignatureElement
| ScriptElementKind ::EnumElement
| ScriptElementKind ::InterfaceElement = > {
commit_characters . push ( " . " ) ;
commit_characters . push ( " ; " ) ;
}
ScriptElementKind ::ModuleElement
| ScriptElementKind ::Alias
| ScriptElementKind ::ConstElement
| ScriptElementKind ::LetElement
| ScriptElementKind ::VariableElement
| ScriptElementKind ::LocalVariableElement
| ScriptElementKind ::MemberVariableElement
| ScriptElementKind ::ClassElement
| ScriptElementKind ::FunctionElement
| ScriptElementKind ::MemberFunctionElement
| ScriptElementKind ::Keyword
| ScriptElementKind ::ParameterElement = > {
commit_characters . push ( " . " ) ;
commit_characters . push ( " , " ) ;
commit_characters . push ( " ; " ) ;
if ! settings . complete_function_calls {
commit_characters . push ( " ( " ) ;
}
}
_ = > ( ) ,
2020-12-08 05:36:13 -05:00
}
2021-03-15 18:01:41 -04:00
if commit_characters . is_empty ( ) {
None
} else {
Some ( commit_characters . into_iter ( ) . map ( String ::from ) . collect ( ) )
}
}
fn get_filter_text ( & self ) -> Option < String > {
2021-09-15 22:07:52 -04:00
if self . name . starts_with ( '#' ) {
if let Some ( insert_text ) = & self . insert_text {
if insert_text . starts_with ( " this.# " ) {
return Some ( insert_text . replace ( " this.# " , " " ) ) ;
} else {
return Some ( insert_text . clone ( ) ) ;
}
} else {
2022-12-29 16:22:47 -05:00
return None ;
2021-09-15 22:07:52 -04:00
}
2021-03-15 18:01:41 -04:00
}
if let Some ( insert_text ) = & self . insert_text {
if insert_text . starts_with ( " this. " ) {
return None ;
}
if insert_text . starts_with ( '[' ) {
2021-09-15 22:07:52 -04:00
return Some (
BRACKET_ACCESSOR_RE
. replace ( insert_text , | caps : & Captures | format! ( " . {} " , & caps [ 1 ] ) )
. to_string ( ) ,
) ;
2020-12-08 05:36:13 -05:00
}
}
2021-03-15 18:01:41 -04:00
self . insert_text . clone ( )
}
2020-12-08 05:36:13 -05:00
2021-03-15 18:01:41 -04:00
pub fn as_completion_item (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-03-15 18:01:41 -04:00
info : & CompletionInfo ,
settings : & config ::CompletionSettings ,
specifier : & ModuleSpecifier ,
position : u32 ,
2023-09-16 10:51:35 -04:00
language_server : & language_server ::Inner ,
2021-03-15 18:01:41 -04:00
) -> lsp ::CompletionItem {
let mut label = self . name . clone ( ) ;
2023-09-16 10:51:35 -04:00
let mut label_details : Option < lsp ::CompletionItemLabelDetails > = None ;
2021-03-15 18:01:41 -04:00
let mut kind : Option < lsp ::CompletionItemKind > =
Some ( self . kind . clone ( ) . into ( ) ) ;
2023-09-18 19:59:26 -04:00
let mut specifier_rewrite = None ;
2020-12-08 05:36:13 -05:00
2023-09-18 14:55:24 -04:00
let mut sort_text = if self . source . is_some ( ) {
format! ( " \u{ffff} {} " , self . sort_text )
2021-03-15 18:01:41 -04:00
} else {
2023-09-18 14:55:24 -04:00
self . sort_text . clone ( )
2021-03-15 18:01:41 -04:00
} ;
let preselect = self . is_recommended ;
let use_code_snippet = settings . complete_function_calls
2021-11-24 20:10:12 -05:00
& & ( kind = = Some ( lsp ::CompletionItemKind ::FUNCTION )
| | kind = = Some ( lsp ::CompletionItemKind ::METHOD ) ) ;
2022-07-11 19:35:18 -04:00
let commit_characters = self . get_commit_characters ( info , settings ) ;
2021-03-15 18:01:41 -04:00
let mut insert_text = self . insert_text . clone ( ) ;
2022-10-14 08:04:38 -04:00
let insert_text_format = match self . is_snippet {
Some ( true ) = > Some ( lsp ::InsertTextFormat ::SNIPPET ) ,
_ = > None ,
} ;
2021-03-15 18:01:41 -04:00
let range = self . replacement_span . clone ( ) ;
let mut filter_text = self . get_filter_text ( ) ;
let mut tags = None ;
let mut detail = None ;
if let Some ( kind_modifiers ) = & self . kind_modifiers {
let kind_modifiers = parse_kind_modifier ( kind_modifiers ) ;
if kind_modifiers . contains ( " optional " ) {
2020-12-08 05:36:13 -05:00
if insert_text . is_none ( ) {
2021-03-15 18:01:41 -04:00
insert_text = Some ( label . clone ( ) ) ;
2020-12-08 05:36:13 -05:00
}
2021-03-15 18:01:41 -04:00
if filter_text . is_none ( ) {
filter_text = Some ( label . clone ( ) ) ;
}
label + = " ? " ;
}
if kind_modifiers . contains ( " deprecated " ) {
2021-11-24 20:10:12 -05:00
tags = Some ( vec! [ lsp ::CompletionItemTag ::DEPRECATED ] ) ;
2021-03-15 18:01:41 -04:00
}
if kind_modifiers . contains ( " color " ) {
2021-11-24 20:10:12 -05:00
kind = Some ( lsp ::CompletionItemKind ::COLOR ) ;
2021-03-15 18:01:41 -04:00
}
if self . kind = = ScriptElementKind ::ScriptElement {
for ext_modifier in FILE_EXTENSION_KIND_MODIFIERS {
if kind_modifiers . contains ( ext_modifier ) {
detail = if self . name . to_lowercase ( ) . ends_with ( ext_modifier ) {
Some ( self . name . clone ( ) )
} else {
Some ( format! ( " {} {} " , self . name , ext_modifier ) )
} ;
break ;
}
2020-12-08 05:36:13 -05:00
}
}
}
2023-09-16 10:51:35 -04:00
if let Some ( source ) = & self . source {
2023-09-18 19:59:26 -04:00
let mut display_source = source . clone ( ) ;
2023-09-16 10:51:35 -04:00
if let Some ( data ) = & self . data {
if let Ok ( import_data ) =
serde_json ::from_value ::< CompletionEntryDataImport > ( data . clone ( ) )
{
if let Ok ( import_specifier ) =
normalize_specifier ( import_data . file_name )
{
if let Some ( new_module_specifier ) = language_server
. get_ts_response_import_mapper ( )
. check_specifier ( & import_specifier , specifier )
. or_else ( | | relative_specifier ( specifier , & import_specifier ) )
{
2023-09-18 19:59:26 -04:00
display_source = new_module_specifier . clone ( ) ;
if new_module_specifier ! = import_data . module_specifier {
specifier_rewrite =
Some ( ( import_data . module_specifier , new_module_specifier ) ) ;
}
2023-09-16 10:51:35 -04:00
}
}
}
}
2023-09-18 14:55:24 -04:00
// We want relative or bare (import-mapped or otherwise) specifiers to
// appear at the top.
2023-09-18 19:59:26 -04:00
if resolve_url ( & display_source ) . is_err ( ) {
2023-09-18 14:55:24 -04:00
sort_text + = " _0 " ;
} else {
sort_text + = " _1 " ;
}
2023-09-16 10:51:35 -04:00
label_details
. get_or_insert_with ( Default ::default )
2023-09-18 19:59:26 -04:00
. description = Some ( display_source ) ;
2023-09-16 10:51:35 -04:00
}
2021-03-15 18:01:41 -04:00
let text_edit =
2021-04-01 05:18:51 -04:00
if let ( Some ( text_span ) , Some ( new_text ) ) = ( range , & insert_text ) {
2021-03-15 18:01:41 -04:00
let range = text_span . to_range ( line_index ) ;
let insert_replace_edit = lsp ::InsertReplaceEdit {
2021-04-01 05:18:51 -04:00
new_text : new_text . clone ( ) ,
2021-03-15 18:01:41 -04:00
insert : range ,
replace : range ,
} ;
Some ( insert_replace_edit . into ( ) )
2020-12-08 05:36:13 -05:00
} else {
2021-03-15 18:01:41 -04:00
None
} ;
2021-03-24 20:13:37 -04:00
let tsc = CompletionItemData {
2021-03-15 18:01:41 -04:00
specifier : specifier . clone ( ) ,
position ,
name : self . name . clone ( ) ,
source : self . source . clone ( ) ,
2023-09-18 19:59:26 -04:00
specifier_rewrite ,
2021-03-15 18:01:41 -04:00
data : self . data . clone ( ) ,
use_code_snippet ,
} ;
2020-12-08 05:36:13 -05:00
2021-03-15 18:01:41 -04:00
lsp ::CompletionItem {
label ,
2023-09-16 10:51:35 -04:00
label_details ,
2021-03-15 18:01:41 -04:00
kind ,
2023-09-18 14:55:24 -04:00
sort_text : Some ( sort_text ) ,
2021-03-15 18:01:41 -04:00
preselect ,
text_edit ,
filter_text ,
2021-04-01 05:18:51 -04:00
insert_text ,
2022-10-14 08:04:38 -04:00
insert_text_format ,
2021-03-15 18:01:41 -04:00
detail ,
tags ,
2022-07-11 19:35:18 -04:00
commit_characters ,
data : Some ( json! ( { " tsc " : tsc } ) ) ,
2021-03-15 18:01:41 -04:00
.. Default ::default ( )
}
2020-12-08 05:36:13 -05:00
}
}
2022-05-31 20:19:18 -04:00
#[ derive(Debug, Default, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct CompletionEntryLabelDetails {
#[ serde(skip_serializing_if = " Option::is_none " ) ]
detail : Option < String > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
description : Option < String > ,
}
2021-04-02 02:21:07 -04:00
#[ derive(Debug, Deserialize) ]
pub enum OutliningSpanKind {
#[ serde(rename = " comment " ) ]
Comment ,
#[ serde(rename = " region " ) ]
Region ,
#[ serde(rename = " code " ) ]
Code ,
#[ serde(rename = " imports " ) ]
Imports ,
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct OutliningSpan {
text_span : TextSpan ,
2022-02-23 16:01:20 -05:00
// hint_span: TextSpan,
// banner_text: String,
// auto_collapse: bool,
2021-04-02 02:21:07 -04:00
kind : OutliningSpanKind ,
}
const FOLD_END_PAIR_CHARACTERS : & [ u8 ] = & [ b '}' , b ']' , b ')' , b '`' ] ;
impl OutliningSpan {
pub fn to_folding_range (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-04-02 02:21:07 -04:00
content : & [ u8 ] ,
line_folding_only : bool ,
) -> lsp ::FoldingRange {
2021-10-28 19:56:01 -04:00
let range = self . text_span . to_range ( line_index . clone ( ) ) ;
2021-04-02 02:21:07 -04:00
lsp ::FoldingRange {
start_line : range . start . line ,
start_character : if line_folding_only {
None
} else {
Some ( range . start . character )
} ,
end_line : self . adjust_folding_end_line (
& range ,
line_index ,
content ,
line_folding_only ,
) ,
end_character : if line_folding_only {
None
} else {
Some ( range . end . character )
} ,
kind : self . get_folding_range_kind ( & self . kind ) ,
2023-09-26 16:57:14 -04:00
collapsed_text : None ,
2021-04-02 02:21:07 -04:00
}
}
fn adjust_folding_end_line (
& self ,
range : & lsp ::Range ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-04-02 02:21:07 -04:00
content : & [ u8 ] ,
line_folding_only : bool ,
) -> u32 {
2021-04-05 23:27:27 -04:00
if line_folding_only & & range . end . line > 0 & & range . end . character > 0 {
2021-04-02 02:21:07 -04:00
let offset_end : usize = line_index . offset ( range . end ) . unwrap ( ) . into ( ) ;
let fold_end_char = content [ offset_end - 1 ] ;
if FOLD_END_PAIR_CHARACTERS . contains ( & fold_end_char ) {
return cmp ::max ( range . end . line - 1 , range . start . line ) ;
}
}
range . end . line
}
fn get_folding_range_kind (
& self ,
span_kind : & OutliningSpanKind ,
) -> Option < lsp ::FoldingRangeKind > {
match span_kind {
OutliningSpanKind ::Comment = > Some ( lsp ::FoldingRangeKind ::Comment ) ,
OutliningSpanKind ::Region = > Some ( lsp ::FoldingRangeKind ::Region ) ,
OutliningSpanKind ::Imports = > Some ( lsp ::FoldingRangeKind ::Imports ) ,
_ = > None ,
}
}
}
2021-02-15 21:34:09 -05:00
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SignatureHelpItems {
items : Vec < SignatureHelpItem > ,
2022-02-23 16:01:20 -05:00
// applicable_span: TextSpan,
2021-02-15 21:34:09 -05:00
selected_item_index : u32 ,
argument_index : u32 ,
2022-02-23 16:01:20 -05:00
// argument_count: u32,
2021-02-15 21:34:09 -05:00
}
impl SignatureHelpItems {
2022-03-23 09:54:22 -04:00
pub fn into_signature_help (
2022-02-09 18:08:53 -05:00
self ,
language_server : & language_server ::Inner ,
) -> lsp ::SignatureHelp {
2021-02-15 21:34:09 -05:00
lsp ::SignatureHelp {
signatures : self
. items
. into_iter ( )
2022-02-09 18:08:53 -05:00
. map ( | item | item . into_signature_information ( language_server ) )
2021-02-15 21:34:09 -05:00
. collect ( ) ,
active_parameter : Some ( self . argument_index ) ,
active_signature : Some ( self . selected_item_index ) ,
}
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SignatureHelpItem {
2022-02-23 16:01:20 -05:00
// is_variadic: bool,
2021-02-15 21:34:09 -05:00
prefix_display_parts : Vec < SymbolDisplayPart > ,
suffix_display_parts : Vec < SymbolDisplayPart > ,
2022-02-23 16:01:20 -05:00
// separator_display_parts: Vec<SymbolDisplayPart>,
2021-02-15 21:34:09 -05:00
parameters : Vec < SignatureHelpParameter > ,
documentation : Vec < SymbolDisplayPart > ,
2022-02-23 16:01:20 -05:00
// tags: Vec<JsDocTagInfo>,
2021-02-15 21:34:09 -05:00
}
impl SignatureHelpItem {
2022-03-23 09:54:22 -04:00
pub fn into_signature_information (
2022-02-09 18:08:53 -05:00
self ,
language_server : & language_server ::Inner ,
) -> lsp ::SignatureInformation {
let prefix_text =
display_parts_to_string ( & self . prefix_display_parts , language_server ) ;
2021-02-15 21:34:09 -05:00
let params_text = self
. parameters
. iter ( )
2022-02-09 18:08:53 -05:00
. map ( | param | {
display_parts_to_string ( & param . display_parts , language_server )
} )
2021-02-15 21:34:09 -05:00
. collect ::< Vec < String > > ( )
. join ( " , " ) ;
2022-02-09 18:08:53 -05:00
let suffix_text =
display_parts_to_string ( & self . suffix_display_parts , language_server ) ;
let documentation =
display_parts_to_string ( & self . documentation , language_server ) ;
2021-02-15 21:34:09 -05:00
lsp ::SignatureInformation {
2023-01-27 10:43:16 -05:00
label : format ! ( " {prefix_text}{params_text}{suffix_text} " ) ,
2021-11-07 17:26:11 -05:00
documentation : Some ( lsp ::Documentation ::MarkupContent (
lsp ::MarkupContent {
kind : lsp ::MarkupKind ::Markdown ,
value : documentation ,
} ,
) ) ,
2021-02-15 21:34:09 -05:00
parameters : Some (
self
. parameters
. into_iter ( )
2022-02-09 18:08:53 -05:00
. map ( | param | param . into_parameter_information ( language_server ) )
2021-02-15 21:34:09 -05:00
. collect ( ) ,
) ,
active_parameter : None ,
}
}
}
#[ derive(Debug, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SignatureHelpParameter {
2022-02-23 16:01:20 -05:00
// name: String,
2021-02-15 21:34:09 -05:00
documentation : Vec < SymbolDisplayPart > ,
display_parts : Vec < SymbolDisplayPart > ,
2022-02-23 16:01:20 -05:00
// is_optional: bool,
2021-02-15 21:34:09 -05:00
}
impl SignatureHelpParameter {
2022-03-23 09:54:22 -04:00
pub fn into_parameter_information (
2022-02-09 18:08:53 -05:00
self ,
language_server : & language_server ::Inner ,
) -> lsp ::ParameterInformation {
let documentation =
display_parts_to_string ( & self . documentation , language_server ) ;
2021-02-15 21:34:09 -05:00
lsp ::ParameterInformation {
label : lsp ::ParameterLabel ::Simple ( display_parts_to_string (
2021-03-15 18:01:41 -04:00
& self . display_parts ,
2022-02-09 18:08:53 -05:00
language_server ,
2021-02-15 21:34:09 -05:00
) ) ,
2021-11-07 17:26:11 -05:00
documentation : Some ( lsp ::Documentation ::MarkupContent (
lsp ::MarkupContent {
kind : lsp ::MarkupKind ::Markdown ,
value : documentation ,
} ,
) ) ,
2021-02-15 21:34:09 -05:00
}
}
}
2021-03-23 19:33:25 -04:00
#[ derive(Debug, Clone, Deserialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SelectionRange {
text_span : TextSpan ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
parent : Option < Box < SelectionRange > > ,
}
impl SelectionRange {
pub fn to_selection_range (
& self ,
2021-10-28 19:56:01 -04:00
line_index : Arc < LineIndex > ,
2021-03-23 19:33:25 -04:00
) -> lsp ::SelectionRange {
lsp ::SelectionRange {
2021-10-28 19:56:01 -04:00
range : self . text_span . to_range ( line_index . clone ( ) ) ,
2021-03-25 14:17:37 -04:00
parent : self . parent . as_ref ( ) . map ( | parent_selection | {
Box ::new ( parent_selection . to_selection_range ( line_index ) )
} ) ,
2021-03-23 19:33:25 -04:00
}
}
}
2020-12-07 05:46:39 -05:00
#[ derive(Debug, Clone, Deserialize) ]
struct Response {
2022-02-23 16:01:20 -05:00
// id: usize,
2020-12-07 05:46:39 -05:00
data : Value ,
}
2022-05-15 14:41:37 -04:00
struct State {
2020-12-07 05:46:39 -05:00
last_id : usize ,
2022-01-17 17:09:43 -05:00
performance : Arc < Performance > ,
2020-12-07 05:46:39 -05:00
response : Option < Response > ,
2021-11-18 13:50:24 -05:00
state_snapshot : Arc < StateSnapshot > ,
2023-09-19 11:37:27 -04:00
normalized_specifiers : HashMap < String , ModuleSpecifier > ,
denormalized_specifiers : HashMap < ModuleSpecifier , String > ,
2022-02-02 09:25:22 -05:00
token : CancellationToken ,
2020-12-07 05:46:39 -05:00
}
2022-05-15 14:41:37 -04:00
impl State {
2022-01-17 17:09:43 -05:00
fn new (
state_snapshot : Arc < StateSnapshot > ,
performance : Arc < Performance > ,
) -> Self {
2020-12-07 05:46:39 -05:00
Self {
last_id : 1 ,
2022-01-17 17:09:43 -05:00
performance ,
2020-12-07 05:46:39 -05:00
response : None ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2023-09-19 11:37:27 -04:00
normalized_specifiers : HashMap ::default ( ) ,
denormalized_specifiers : HashMap ::default ( ) ,
2022-02-02 09:25:22 -05:00
token : Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
}
}
2021-06-21 17:18:32 -04:00
2023-09-19 11:37:27 -04:00
/// Convert the specifier to one compatible with tsc. Cache the resulting
/// mapping in case it needs to be reversed.
fn denormalize_specifier ( & mut self , specifier : & ModuleSpecifier ) -> String {
let original = specifier ;
if let Some ( specifier ) = self . denormalized_specifiers . get ( original ) {
return specifier . to_string ( ) ;
}
let mut specifier = original . to_string ( ) ;
let media_type = MediaType ::from_specifier ( original ) ;
// If the URL-inferred media type doesn't correspond to tsc's path-inferred
// media type, force it to be the same by appending an extension.
if MediaType ::from_path ( Path ::new ( specifier . as_str ( ) ) ) ! = media_type {
specifier + = media_type . as_ts_extension ( ) ;
}
if specifier ! = original . as_str ( ) {
self
. normalized_specifiers
. insert ( specifier . clone ( ) , original . clone ( ) ) ;
}
specifier
2021-06-21 17:18:32 -04:00
}
2023-09-19 11:37:27 -04:00
/// Convert the specifier from one compatible with tsc. Cache the resulting
/// mapping in case it needs to be reversed.
2021-06-21 17:18:32 -04:00
fn normalize_specifier < S : AsRef < str > > (
& mut self ,
specifier : S ,
) -> Result < ModuleSpecifier , AnyError > {
2023-09-19 11:37:27 -04:00
let original = specifier . as_ref ( ) ;
if let Some ( specifier ) = self . normalized_specifiers . get ( original ) {
return Ok ( specifier . clone ( ) ) ;
}
let specifier_str = original . replace ( " .d.ts.d.ts " , " .d.ts " ) ;
let specifier = match ModuleSpecifier ::parse ( & specifier_str ) {
Ok ( s ) = > s ,
Err ( err ) = > return Err ( err . into ( ) ) ,
} ;
if specifier . as_str ( ) ! = original {
2021-06-21 17:18:32 -04:00
self
2023-09-19 11:37:27 -04:00
. denormalized_specifiers
. insert ( specifier . clone ( ) , original . to_string ( ) ) ;
2021-06-21 17:18:32 -04:00
}
2023-09-19 11:37:27 -04:00
Ok ( specifier )
2021-06-21 17:18:32 -04:00
}
2020-12-07 05:46:39 -05:00
2022-05-15 14:41:37 -04:00
fn get_asset_or_document (
& self ,
specifier : & ModuleSpecifier ,
) -> Option < AssetOrDocument > {
let snapshot = & self . state_snapshot ;
if specifier . scheme ( ) = = " asset " {
snapshot . assets . get ( specifier ) . map ( AssetOrDocument ::Asset )
} else {
snapshot
. documents
. get ( specifier )
. map ( AssetOrDocument ::Document )
}
}
fn script_version ( & self , specifier : & ModuleSpecifier ) -> Option < String > {
if specifier . scheme ( ) = = " asset " {
if self . state_snapshot . assets . contains_key ( specifier ) {
Some ( " 1 " . to_string ( ) )
} else {
None
}
} else {
self
. state_snapshot
. documents
. get ( specifier )
. map ( | d | d . script_version ( ) )
}
2020-12-07 05:46:39 -05:00
}
}
2021-06-21 17:18:32 -04:00
fn normalize_specifier < S : AsRef < str > > (
specifier : S ,
) -> Result < ModuleSpecifier , AnyError > {
resolve_url ( specifier . as_ref ( ) . replace ( " .d.ts.d.ts " , " .d.ts " ) . as_str ( ) )
. map_err ( | err | err . into ( ) )
}
#[ derive(Debug, Deserialize, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct SpecifierArgs {
specifier : String ,
}
2023-09-12 07:14:45 -04:00
#[ op2(fast) ]
2022-05-13 04:36:31 -04:00
fn op_is_cancelled ( state : & mut OpState ) -> bool {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2022-05-13 04:36:31 -04:00
state . token . is_cancelled ( )
2022-02-02 09:25:22 -05:00
}
2023-09-12 07:14:45 -04:00
#[ op2(fast) ]
fn op_is_node_file ( state : & mut OpState , #[ string ] path : String ) -> bool {
2022-10-21 11:20:18 -04:00
let state = state . borrow ::< State > ( ) ;
match ModuleSpecifier ::parse ( & path ) {
Ok ( specifier ) = > state
. state_snapshot
. maybe_npm_resolver
. as_ref ( )
. map ( | r | r . in_npm_package ( & specifier ) )
. unwrap_or ( false ) ,
Err ( _ ) = > false ,
}
}
2023-09-11 20:55:57 -04:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
struct LoadResponse {
data : Arc < str > ,
script_kind : i32 ,
version : Option < String > ,
}
2023-09-12 07:14:45 -04:00
#[ op2 ]
#[ serde ]
2021-06-21 17:18:32 -04:00
fn op_load (
2022-03-14 13:44:15 -04:00
state : & mut OpState ,
2023-09-12 07:14:45 -04:00
#[ serde ] args : SpecifierArgs ,
2023-09-11 20:55:57 -04:00
) -> Result < Option < LoadResponse > , AnyError > {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2022-01-17 17:09:43 -05:00
let mark = state . performance . mark ( " op_load " , Some ( & args ) ) ;
2021-06-21 17:18:32 -04:00
let specifier = state . normalize_specifier ( args . specifier ) ? ;
2022-05-15 14:41:37 -04:00
let asset_or_document = state . get_asset_or_document ( & specifier ) ;
2022-01-17 17:09:43 -05:00
state . performance . measure ( mark ) ;
2023-09-11 20:55:57 -04:00
Ok ( asset_or_document . map ( | doc | LoadResponse {
data : doc . text ( ) ,
script_kind : crate ::tsc ::as_ts_script_kind ( doc . media_type ( ) ) ,
version : state . script_version ( & specifier ) ,
} ) )
2021-06-21 17:18:32 -04:00
}
2023-09-12 07:14:45 -04:00
#[ op2 ]
#[ serde ]
2021-05-02 08:29:19 -04:00
fn op_resolve (
2022-03-14 13:44:15 -04:00
state : & mut OpState ,
2023-09-12 07:14:45 -04:00
#[ serde ] args : ResolveArgs ,
2021-02-24 22:15:55 -05:00
) -> Result < Vec < Option < ( String , String ) > > , AnyError > {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2022-01-17 17:09:43 -05:00
let mark = state . performance . mark ( " op_resolve " , Some ( & args ) ) ;
2021-06-21 17:18:32 -04:00
let referrer = state . normalize_specifier ( & args . base ) ? ;
2023-01-24 09:05:54 -05:00
let result = match state . get_asset_or_document ( & referrer ) {
Some ( referrer_doc ) = > {
let resolved = state . state_snapshot . documents . resolve (
args . specifiers ,
& referrer_doc ,
2023-04-17 15:36:23 -04:00
state . state_snapshot . maybe_node_resolver . as_ref ( ) ,
2023-01-24 09:05:54 -05:00
) ;
Ok (
resolved
. into_iter ( )
. map ( | o | {
2023-09-19 11:37:27 -04:00
o . map ( | ( s , mt ) | {
(
state . denormalize_specifier ( & s ) ,
mt . as_ts_extension ( ) . to_string ( ) ,
)
} )
2023-01-24 09:05:54 -05:00
} )
. collect ( ) ,
)
}
2023-03-31 12:48:18 -04:00
None = > {
lsp_warn! (
2021-10-28 19:56:01 -04:00
" Error resolving. Referring specifier \" {} \" was not found. " ,
args . base
2023-03-31 12:48:18 -04:00
) ;
Ok ( vec! [ None ; args . specifiers . len ( ) ] )
}
2021-10-28 19:56:01 -04:00
} ;
2020-12-07 05:46:39 -05:00
2022-01-17 17:09:43 -05:00
state . performance . measure ( mark ) ;
2021-10-28 19:56:01 -04:00
result
2020-12-07 05:46:39 -05:00
}
2023-09-12 07:14:45 -04:00
#[ op2 ]
fn op_respond ( state : & mut OpState , #[ serde ] args : Response ) {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2021-02-24 22:15:55 -05:00
state . response = Some ( args ) ;
2020-12-07 05:46:39 -05:00
}
2023-09-12 07:14:45 -04:00
#[ op2 ]
#[ serde ]
2023-01-24 09:05:54 -05:00
fn op_script_names ( state : & mut OpState ) -> Vec < String > {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2023-01-24 09:05:54 -05:00
let documents = & state . state_snapshot . documents ;
2023-03-30 17:47:53 -04:00
let all_docs = documents . documents ( DocumentsFilter ::AllDiagnosable ) ;
2023-03-11 11:43:45 -05:00
let mut seen = HashSet ::new ( ) ;
2023-03-30 17:47:53 -04:00
let mut result = Vec ::new ( ) ;
2023-01-24 09:05:54 -05:00
if documents . has_injected_types_node_package ( ) {
// ensure this is first so it resolves the node types first
2023-03-11 11:43:45 -05:00
let specifier = " asset:///node_types.d.ts " ;
result . push ( specifier . to_string ( ) ) ;
seen . insert ( specifier ) ;
}
// inject these next because they're global
for import in documents . module_graph_imports ( ) {
if seen . insert ( import . as_str ( ) ) {
result . push ( import . to_string ( ) ) ;
}
}
// finally include the documents and all their dependencies
2023-03-30 17:47:53 -04:00
for doc in & all_docs {
let specifiers = std ::iter ::once ( doc . specifier ( ) ) . chain (
doc
. dependencies ( )
. values ( )
. filter_map ( | dep | dep . get_type ( ) . or_else ( | | dep . get_code ( ) ) ) ,
) ;
for specifier in specifiers {
2023-08-02 16:57:25 -04:00
if seen . insert ( specifier . as_str ( ) ) {
if let Some ( specifier ) = documents . resolve_redirected ( specifier ) {
// only include dependencies we know to exist otherwise typescript will error
if documents . exists ( & specifier ) {
result . push ( specifier . to_string ( ) ) ;
}
}
2023-03-11 11:43:45 -05:00
}
}
2023-01-24 09:05:54 -05:00
}
result
2020-12-07 05:46:39 -05:00
}
2021-05-11 00:54:10 -04:00
#[ derive(Debug, Deserialize, Serialize) ]
2020-12-07 05:46:39 -05:00
#[ serde(rename_all = " camelCase " ) ]
struct ScriptVersionArgs {
specifier : String ,
}
2023-09-12 07:14:45 -04:00
#[ op2 ]
#[ string ]
2021-05-02 08:29:19 -04:00
fn op_script_version (
2022-03-14 13:44:15 -04:00
state : & mut OpState ,
2023-09-12 07:14:45 -04:00
#[ serde ] args : ScriptVersionArgs ,
2021-02-24 22:15:55 -05:00
) -> Result < Option < String > , AnyError > {
2022-03-14 13:44:15 -04:00
let state = state . borrow_mut ::< State > ( ) ;
2022-01-24 03:01:33 -05:00
// this op is very "noisy" and measuring its performance is not useful, so we
// don't measure it uniquely anymore.
2021-06-21 17:18:32 -04:00
let specifier = state . normalize_specifier ( args . specifier ) ? ;
2022-05-15 14:41:37 -04:00
Ok ( state . script_version ( & specifier ) )
2020-12-07 05:46:39 -05:00
}
/// Create and setup a JsRuntime based on a snapshot. It is expected that the
/// supplied snapshot is an isolate that contains the TypeScript language
/// server.
2023-08-01 20:49:09 -04:00
fn js_runtime (
performance : Arc < Performance > ,
cache : Arc < dyn HttpCache > ,
) -> JsRuntime {
2021-12-29 09:22:00 -05:00
JsRuntime ::new ( RuntimeOptions {
2023-07-08 16:06:45 -04:00
extensions : vec ! [ deno_tsc ::init_ops ( performance , cache ) ] ,
2022-07-04 18:12:41 -04:00
startup_snapshot : Some ( tsc ::compiler_snapshot ( ) ) ,
2020-12-07 05:46:39 -05:00
.. Default ::default ( )
2021-12-29 09:22:00 -05:00
} )
}
2020-12-07 05:46:39 -05:00
2023-03-17 14:22:15 -04:00
deno_core ::extension! ( deno_tsc ,
ops = [
op_is_cancelled ,
op_is_node_file ,
op_load ,
op_resolve ,
op_respond ,
op_script_names ,
op_script_version ,
] ,
2023-03-17 18:15:27 -04:00
options = {
2023-07-08 16:06:45 -04:00
performance : Arc < Performance > ,
2023-08-01 20:49:09 -04:00
cache : Arc < dyn HttpCache > ,
2023-03-17 14:22:15 -04:00
} ,
2023-03-17 18:15:27 -04:00
state = | state , options | {
2023-03-17 14:22:15 -04:00
state . put ( State ::new (
2023-07-08 16:06:45 -04:00
Arc ::new ( StateSnapshot {
assets : Default ::default ( ) ,
cache_metadata : CacheMetadata ::new ( options . cache . clone ( ) ) ,
2023-09-09 14:37:01 -04:00
config : Default ::default ( ) ,
2023-07-08 16:06:45 -04:00
documents : Documents ::new ( options . cache . clone ( ) ) ,
maybe_import_map : None ,
maybe_node_resolver : None ,
maybe_npm_resolver : None ,
} ) ,
2023-03-17 18:15:27 -04:00
options . performance ,
2023-03-17 14:22:15 -04:00
) ) ;
} ,
) ;
2021-06-21 17:18:32 -04:00
/// Instruct a language server runtime to start the language server and provide
/// it with a minimal bootstrap configuration.
2023-03-11 11:43:45 -05:00
fn start ( runtime : & mut JsRuntime , debug : bool ) -> Result < ( ) , AnyError > {
let init_config = json! ( { " debug " : debug } ) ;
2023-01-27 10:43:16 -05:00
let init_src = format! ( " globalThis.serverInit( {init_config} ); " ) ;
2020-12-07 05:46:39 -05:00
2023-04-04 08:46:31 -04:00
runtime . execute_script ( located_script_name! ( ) , init_src . into ( ) ) ? ;
2021-07-08 12:56:53 -04:00
Ok ( ( ) )
2020-12-07 05:46:39 -05:00
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Deserialize_repr, Serialize_repr) ]
#[ repr(u32) ]
pub enum CompletionTriggerKind {
Invoked = 1 ,
TriggerCharacter = 2 ,
TriggerForIncompleteCompletions = 3 ,
}
impl From < lsp ::CompletionTriggerKind > for CompletionTriggerKind {
fn from ( kind : lsp ::CompletionTriggerKind ) -> Self {
match kind {
lsp ::CompletionTriggerKind ::INVOKED = > Self ::Invoked ,
lsp ::CompletionTriggerKind ::TRIGGER_CHARACTER = > Self ::TriggerCharacter ,
lsp ::CompletionTriggerKind ::TRIGGER_FOR_INCOMPLETE_COMPLETIONS = > {
Self ::TriggerForIncompleteCompletions
}
_ = > Self ::Invoked ,
}
}
}
2020-12-08 05:36:13 -05:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " kebab-case " ) ]
#[ allow(dead_code) ]
pub enum QuotePreference {
Auto ,
Double ,
Single ,
}
2023-09-18 15:48:32 -04:00
impl From < & FmtOptionsConfig > for QuotePreference {
fn from ( config : & FmtOptionsConfig ) -> Self {
match config . single_quote {
Some ( true ) = > QuotePreference ::Single ,
_ = > QuotePreference ::Double ,
}
}
}
2023-09-25 22:54:07 -04:00
pub type ImportModuleSpecifierPreference = config ::ImportModuleSpecifier ;
2020-12-08 05:36:13 -05:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " kebab-case " ) ]
#[ allow(dead_code) ]
pub enum ImportModuleSpecifierEnding {
Auto ,
Minimal ,
Index ,
Js ,
}
2022-07-11 19:35:18 -04:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " kebab-case " ) ]
#[ allow(dead_code) ]
pub enum IncludeInlayParameterNameHints {
None ,
Literals ,
All ,
}
2022-10-15 22:39:43 -04:00
impl From < & config ::InlayHintsParamNamesEnabled >
for IncludeInlayParameterNameHints
{
fn from ( setting : & config ::InlayHintsParamNamesEnabled ) -> Self {
match setting {
config ::InlayHintsParamNamesEnabled ::All = > Self ::All ,
config ::InlayHintsParamNamesEnabled ::Literals = > Self ::Literals ,
config ::InlayHintsParamNamesEnabled ::None = > Self ::None ,
}
}
}
2020-12-08 05:36:13 -05:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " kebab-case " ) ]
#[ allow(dead_code) ]
pub enum IncludePackageJsonAutoImports {
Auto ,
On ,
Off ,
}
2023-09-25 22:54:07 -04:00
pub type JsxAttributeCompletionStyle = config ::JsxAttributeCompletionStyle ;
2022-07-11 19:35:18 -04:00
2021-03-15 18:01:41 -04:00
#[ derive(Debug, Default, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct GetCompletionsAtPositionOptions {
#[ serde(flatten) ]
pub user_preferences : UserPreferences ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub trigger_character : Option < String > ,
2022-07-11 19:35:18 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub trigger_kind : Option < CompletionTriggerKind > ,
2021-03-15 18:01:41 -04:00
}
2020-12-08 05:36:13 -05:00
#[ derive(Debug, Default, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct UserPreferences {
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub disable_suggestions : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub quote_preference : Option < QuotePreference > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_completions_for_module_exports : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-09-15 22:07:52 -04:00
pub include_completions_for_import_statements : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_completions_with_snippet_text : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
pub include_automatic_optional_chain_completions : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_completions_with_insert_text : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-07-11 19:35:18 -04:00
pub include_completions_with_class_member_snippets : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_completions_with_object_literal_method_snippets : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub use_label_details_in_completion_entries : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-09-15 22:07:52 -04:00
pub allow_incomplete_completions : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-12-08 05:36:13 -05:00
pub import_module_specifier_preference :
Option < ImportModuleSpecifierPreference > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub import_module_specifier_ending : Option < ImportModuleSpecifierEnding > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub allow_text_changes_in_new_files : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub provide_prefix_and_suffix_text_for_rename : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_package_json_auto_imports : Option < IncludePackageJsonAutoImports > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub provide_refactor_not_applicable_reason : Option < bool > ,
2022-07-11 19:35:18 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub jsx_attribute_completion_style : Option < JsxAttributeCompletionStyle > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_parameter_name_hints :
Option < IncludeInlayParameterNameHints > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_parameter_name_hints_when_argument_matches_name :
Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_function_parameter_type_hints : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_variable_type_hints : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-10-15 22:39:43 -04:00
pub include_inlay_variable_type_hints_when_type_matches_name : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-07-11 19:35:18 -04:00
pub include_inlay_property_declaration_type_hints : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_function_like_return_type_hints : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub include_inlay_enum_member_value_hints : Option < bool > ,
2022-10-14 08:04:38 -04:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub allow_rename_of_import_path : Option < bool > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub auto_import_file_exclude_patterns : Option < Vec < String > > ,
2020-12-08 05:36:13 -05:00
}
2023-09-21 01:46:39 -04:00
impl UserPreferences {
2023-09-25 22:54:07 -04:00
pub fn from_config_for_specifier (
config : & config ::Config ,
fmt_config : & FmtOptionsConfig ,
2023-09-21 01:46:39 -04:00
specifier : & ModuleSpecifier ,
) -> Self {
2023-09-25 22:54:07 -04:00
let base_preferences = Self {
allow_incomplete_completions : Some ( true ) ,
allow_text_changes_in_new_files : Some ( specifier . scheme ( ) = = " file " ) ,
// TODO(nayeemrmn): Investigate why we use `Index` here.
import_module_specifier_ending : Some ( ImportModuleSpecifierEnding ::Index ) ,
include_completions_with_snippet_text : Some (
config . client_capabilities . snippet_support ,
) ,
provide_refactor_not_applicable_reason : Some ( true ) ,
quote_preference : Some ( fmt_config . into ( ) ) ,
use_label_details_in_completion_entries : Some ( true ) ,
2022-10-15 22:39:43 -04:00
.. Default ::default ( )
2023-09-25 22:54:07 -04:00
} ;
let Some ( language_settings ) = config
. workspace_settings ( )
. language_settings_for_specifier ( specifier )
else {
return base_preferences ;
} ;
Self {
auto_import_file_exclude_patterns : Some (
language_settings
. preferences
. auto_import_file_exclude_patterns
. clone ( ) ,
) ,
include_automatic_optional_chain_completions : Some (
language_settings . suggest . enabled
& & language_settings
. suggest
. include_automatic_optional_chain_completions ,
) ,
include_completions_for_import_statements : Some (
language_settings . suggest . enabled
& & language_settings
. suggest
. include_completions_for_import_statements ,
) ,
include_completions_for_module_exports : Some (
language_settings . suggest . enabled
& & language_settings . suggest . auto_imports ,
) ,
include_completions_with_class_member_snippets : Some (
language_settings . suggest . enabled
& & language_settings . suggest . class_member_snippets . enabled
& & config . client_capabilities . snippet_support ,
) ,
include_completions_with_insert_text : Some (
language_settings . suggest . enabled ,
) ,
include_completions_with_object_literal_method_snippets : Some (
language_settings . suggest . enabled
& & language_settings
. suggest
. object_literal_method_snippets
. enabled
& & config . client_capabilities . snippet_support ,
) ,
import_module_specifier_preference : Some (
language_settings . preferences . import_module_specifier ,
) ,
include_inlay_parameter_name_hints : Some (
( & language_settings . inlay_hints . parameter_names . enabled ) . into ( ) ,
) ,
include_inlay_parameter_name_hints_when_argument_matches_name : Some (
! language_settings
. inlay_hints
. parameter_names
. suppress_when_argument_matches_name ,
) ,
include_inlay_function_parameter_type_hints : Some (
language_settings . inlay_hints . parameter_types . enabled ,
) ,
include_inlay_variable_type_hints : Some (
language_settings . inlay_hints . variable_types . enabled ,
) ,
include_inlay_variable_type_hints_when_type_matches_name : Some (
! language_settings
. inlay_hints
. variable_types
. suppress_when_type_matches_name ,
) ,
include_inlay_property_declaration_type_hints : Some (
language_settings
. inlay_hints
. property_declaration_types
. enabled ,
) ,
include_inlay_function_like_return_type_hints : Some (
language_settings
. inlay_hints
. function_like_return_types
. enabled ,
) ,
include_inlay_enum_member_value_hints : Some (
language_settings . inlay_hints . enum_member_values . enabled ,
) ,
jsx_attribute_completion_style : Some (
language_settings . preferences . jsx_attribute_completion_style ,
) ,
provide_prefix_and_suffix_text_for_rename : Some (
language_settings . preferences . use_aliases_for_renames ,
) ,
.. base_preferences
2022-10-15 22:39:43 -04:00
}
}
}
2021-02-15 21:34:09 -05:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SignatureHelpItemsOptions {
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub trigger_reason : Option < SignatureHelpTriggerReason > ,
}
#[ derive(Debug, Serialize) ]
pub enum SignatureHelpTriggerKind {
#[ serde(rename = " characterTyped " ) ]
CharacterTyped ,
#[ serde(rename = " invoked " ) ]
Invoked ,
#[ serde(rename = " retrigger " ) ]
Retrigger ,
2021-11-24 20:10:12 -05:00
#[ serde(rename = " unknown " ) ]
Unknown ,
2021-02-15 21:34:09 -05:00
}
impl From < lsp ::SignatureHelpTriggerKind > for SignatureHelpTriggerKind {
fn from ( kind : lsp ::SignatureHelpTriggerKind ) -> Self {
match kind {
2021-11-24 20:10:12 -05:00
lsp ::SignatureHelpTriggerKind ::INVOKED = > Self ::Invoked ,
lsp ::SignatureHelpTriggerKind ::TRIGGER_CHARACTER = > Self ::CharacterTyped ,
lsp ::SignatureHelpTriggerKind ::CONTENT_CHANGE = > Self ::Retrigger ,
_ = > Self ::Unknown ,
2021-02-15 21:34:09 -05:00
}
}
}
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct SignatureHelpTriggerReason {
pub kind : SignatureHelpTriggerKind ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub trigger_character : Option < String > ,
}
2021-03-15 18:01:41 -04:00
#[ derive(Debug, Serialize) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct GetCompletionDetailsArgs {
pub specifier : ModuleSpecifier ,
pub position : u32 ,
pub name : String ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2023-08-17 10:46:11 -04:00
pub format_code_settings : Option < FormatCodeSettings > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-03-15 18:01:41 -04:00
pub source : Option < String > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-07-11 19:35:18 -04:00
pub preferences : Option < UserPreferences > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2021-03-15 18:01:41 -04:00
pub data : Option < Value > ,
}
2022-07-11 19:35:18 -04:00
impl From < & CompletionItemData > for GetCompletionDetailsArgs {
fn from ( item_data : & CompletionItemData ) -> Self {
2021-03-15 18:01:41 -04:00
Self {
2022-07-11 19:35:18 -04:00
specifier : item_data . specifier . clone ( ) ,
2021-03-15 18:01:41 -04:00
position : item_data . position ,
2022-07-11 19:35:18 -04:00
name : item_data . name . clone ( ) ,
source : item_data . source . clone ( ) ,
preferences : None ,
2023-08-17 10:46:11 -04:00
format_code_settings : None ,
2022-07-11 19:35:18 -04:00
data : item_data . data . clone ( ) ,
2021-03-15 18:01:41 -04:00
}
}
}
2023-05-12 19:07:40 -04:00
#[ derive(Debug) ]
pub struct GetNavigateToItemsArgs {
pub search : String ,
pub max_result_count : Option < u32 > ,
pub file : Option < String > ,
}
2020-12-07 05:46:39 -05:00
/// Methods that are supported by the Language Service in the compiler isolate.
2021-01-22 05:03:16 -05:00
#[ derive(Debug) ]
2023-05-12 19:07:40 -04:00
enum RequestMethod {
2020-12-07 05:46:39 -05:00
/// Configure the compilation settings for the server.
Configure ( TsConfig ) ,
2021-01-31 22:30:41 -05:00
/// Get rename locations at a given position.
2021-06-19 11:23:50 -04:00
FindRenameLocations {
specifier : ModuleSpecifier ,
position : u32 ,
find_in_strings : bool ,
find_in_comments : bool ,
provide_prefix_and_suffix_text_for_rename : bool ,
} ,
2022-04-25 11:23:24 -04:00
GetAssets ,
2021-08-05 21:46:32 -04:00
/// Retrieve the possible refactor info for a range of a file.
2023-09-18 15:48:32 -04:00
GetApplicableRefactors (
( ModuleSpecifier , TextSpan , Option < UserPreferences > , String ) ,
) ,
2021-08-05 21:46:32 -04:00
/// Retrieve the refactor edit info for a range.
2023-08-17 10:46:11 -04:00
GetEditsForRefactor (
(
ModuleSpecifier ,
FormatCodeSettings ,
TextSpan ,
String ,
String ,
2023-09-18 15:48:32 -04:00
Option < UserPreferences > ,
2023-08-17 10:46:11 -04:00
) ,
) ,
2023-08-25 20:50:47 -04:00
/// Retrieve the refactor edit info for a range.
GetEditsForFileRename (
(
ModuleSpecifier ,
ModuleSpecifier ,
FormatCodeSettings ,
UserPreferences ,
) ,
) ,
2021-02-04 13:53:02 -05:00
/// Retrieve code fixes for a range of a file with the provided error codes.
2023-09-18 15:48:32 -04:00
GetCodeFixes (
(
ModuleSpecifier ,
u32 ,
u32 ,
Vec < String > ,
FormatCodeSettings ,
UserPreferences ,
) ,
) ,
2021-01-31 22:30:41 -05:00
/// Get completion information at a given position (IntelliSense).
2023-08-17 10:46:11 -04:00
GetCompletions (
(
ModuleSpecifier ,
u32 ,
GetCompletionsAtPositionOptions ,
FormatCodeSettings ,
) ,
) ,
2021-03-15 18:01:41 -04:00
/// Get details about a specific completion entry.
GetCompletionDetails ( GetCompletionDetailsArgs ) ,
2021-02-04 13:53:02 -05:00
/// Retrieve the combined code fixes for a fix id for a module.
2023-09-18 15:48:32 -04:00
GetCombinedCodeFix (
( ModuleSpecifier , Value , FormatCodeSettings , UserPreferences ) ,
) ,
2021-01-31 22:30:41 -05:00
/// Get declaration information for a specific position.
GetDefinition ( ( ModuleSpecifier , u32 ) ) ,
2020-12-29 20:46:58 -05:00
/// Return diagnostics for given file.
2021-01-22 05:03:16 -05:00
GetDiagnostics ( Vec < ModuleSpecifier > ) ,
2020-12-07 05:46:39 -05:00
/// Return document highlights at position.
GetDocumentHighlights ( ( ModuleSpecifier , u32 , Vec < ModuleSpecifier > ) ) ,
2021-04-19 21:26:36 -04:00
/// Get semantic highlights information for a particular file.
GetEncodedSemanticClassifications ( ( ModuleSpecifier , TextSpan ) ) ,
2021-01-12 16:53:27 -05:00
/// Get implementation information for a specific position.
GetImplementation ( ( ModuleSpecifier , u32 ) ) ,
2021-11-22 19:08:56 -05:00
/// Get "navigate to" items, which are converted to workspace symbols
2023-05-12 19:07:40 -04:00
GetNavigateToItems ( GetNavigateToItemsArgs ) ,
2021-01-31 22:30:41 -05:00
/// Get a "navigation tree" for a specifier.
GetNavigationTree ( ModuleSpecifier ) ,
2021-04-02 02:21:07 -04:00
/// Get outlining spans for a specifier.
GetOutliningSpans ( ModuleSpecifier ) ,
2021-01-31 22:30:41 -05:00
/// Return quick info at position (hover information).
GetQuickInfo ( ( ModuleSpecifier , u32 ) ) ,
2023-03-30 12:15:21 -04:00
/// Finds the document references for a specific position.
FindReferences {
specifier : ModuleSpecifier ,
position : u32 ,
} ,
2021-02-15 21:34:09 -05:00
/// Get signature help items for a specific position.
GetSignatureHelpItems ( ( ModuleSpecifier , u32 , SignatureHelpItemsOptions ) ) ,
2021-03-23 19:33:25 -04:00
/// Get a selection range for a specific position.
GetSmartSelectionRange ( ( ModuleSpecifier , u32 ) ) ,
2021-02-04 13:53:02 -05:00
/// Get the diagnostic codes that support some form of code fix.
GetSupportedCodeFixes ,
2021-11-22 19:09:19 -05:00
/// Get the type definition information for a specific position.
GetTypeDefinition {
specifier : ModuleSpecifier ,
position : u32 ,
} ,
2021-04-19 01:11:26 -04:00
/// Resolve a call hierarchy item for a specific position.
PrepareCallHierarchy ( ( ModuleSpecifier , u32 ) ) ,
/// Resolve incoming call hierarchy items for a specific position.
ProvideCallHierarchyIncomingCalls ( ( ModuleSpecifier , u32 ) ) ,
/// Resolve outgoing call hierarchy items for a specific position.
ProvideCallHierarchyOutgoingCalls ( ( ModuleSpecifier , u32 ) ) ,
2022-10-15 22:39:43 -04:00
/// Resolve inlay hints for a specific text span
ProvideInlayHints ( ( ModuleSpecifier , TextSpan , UserPreferences ) ) ,
2022-06-27 13:43:43 -04:00
// Special request, used only internally by the LSP
Restart ,
2020-12-07 05:46:39 -05:00
}
impl RequestMethod {
2023-09-19 11:37:27 -04:00
fn to_value ( & self , state : & mut State , id : usize ) -> Value {
2020-12-07 05:46:39 -05:00
match self {
RequestMethod ::Configure ( config ) = > json! ( {
" id " : id ,
" method " : " configure " ,
" compilerOptions " : config ,
} ) ,
2021-06-19 11:23:50 -04:00
RequestMethod ::FindRenameLocations {
2021-01-31 22:30:41 -05:00
specifier ,
position ,
find_in_strings ,
find_in_comments ,
provide_prefix_and_suffix_text_for_rename ,
2021-06-19 11:23:50 -04:00
} = > {
2021-01-31 22:30:41 -05:00
json! ( {
" id " : id ,
" method " : " findRenameLocations " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-01-31 22:30:41 -05:00
" position " : position ,
" findInStrings " : find_in_strings ,
" findInComments " : find_in_comments ,
" providePrefixAndSuffixTextForRename " : provide_prefix_and_suffix_text_for_rename
} )
}
2022-04-25 11:23:24 -04:00
RequestMethod ::GetAssets = > json! ( {
2020-12-15 14:34:39 -05:00
" id " : id ,
2022-04-25 11:23:24 -04:00
" method " : " getAssets " ,
2020-12-15 14:34:39 -05:00
} ) ,
2023-09-18 15:48:32 -04:00
RequestMethod ::GetApplicableRefactors ( (
specifier ,
span ,
preferences ,
kind ,
) ) = > json! ( {
2021-08-05 21:46:32 -04:00
" id " : id ,
" method " : " getApplicableRefactors " ,
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-11-22 19:09:19 -05:00
" range " : { " pos " : span . start , " end " : span . start + span . length } ,
2023-09-18 15:48:32 -04:00
" preferences " : preferences ,
2021-08-05 21:46:32 -04:00
" kind " : kind ,
} ) ,
RequestMethod ::GetEditsForRefactor ( (
specifier ,
2023-08-17 10:46:11 -04:00
format_code_settings ,
2021-08-05 21:46:32 -04:00
span ,
refactor_name ,
action_name ,
2023-09-18 15:48:32 -04:00
preferences ,
2021-08-05 21:46:32 -04:00
) ) = > json! ( {
" id " : id ,
" method " : " getEditsForRefactor " ,
" specifier " : state . denormalize_specifier ( specifier ) ,
2023-08-17 10:46:11 -04:00
" formatCodeSettings " : format_code_settings ,
2021-08-05 21:46:32 -04:00
" range " : { " pos " : span . start , " end " : span . start + span . length } ,
" refactorName " : refactor_name ,
" actionName " : action_name ,
2023-09-18 15:48:32 -04:00
" preferences " : preferences ,
2021-08-05 21:46:32 -04:00
} ) ,
2023-08-25 20:50:47 -04:00
RequestMethod ::GetEditsForFileRename ( (
old_specifier ,
new_specifier ,
format_code_settings ,
preferences ,
) ) = > json! ( {
" id " : id ,
" method " : " getEditsForFileRename " ,
" oldSpecifier " : state . denormalize_specifier ( old_specifier ) ,
" newSpecifier " : state . denormalize_specifier ( new_specifier ) ,
" formatCodeSettings " : format_code_settings ,
" preferences " : preferences ,
} ) ,
2021-02-04 13:53:02 -05:00
RequestMethod ::GetCodeFixes ( (
specifier ,
start_pos ,
end_pos ,
error_codes ,
2023-08-17 10:46:11 -04:00
format_code_settings ,
2023-09-18 15:48:32 -04:00
preferences ,
2021-02-04 13:53:02 -05:00
) ) = > json! ( {
" id " : id ,
" method " : " getCodeFixes " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-02-04 13:53:02 -05:00
" startPosition " : start_pos ,
" endPosition " : end_pos ,
" errorCodes " : error_codes ,
2023-08-17 10:46:11 -04:00
" formatCodeSettings " : format_code_settings ,
2023-09-18 15:48:32 -04:00
" preferences " : preferences ,
2021-02-04 13:53:02 -05:00
} ) ,
2023-08-17 10:46:11 -04:00
RequestMethod ::GetCombinedCodeFix ( (
specifier ,
fix_id ,
format_code_settings ,
2023-09-18 15:48:32 -04:00
preferences ,
2023-08-17 10:46:11 -04:00
) ) = > json! ( {
2021-02-04 13:53:02 -05:00
" id " : id ,
" method " : " getCombinedCodeFix " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-02-04 13:53:02 -05:00
" fixId " : fix_id ,
2023-08-17 10:46:11 -04:00
" formatCodeSettings " : format_code_settings ,
2023-09-18 15:48:32 -04:00
" preferences " : preferences ,
2021-02-04 13:53:02 -05:00
} ) ,
2021-03-15 18:01:41 -04:00
RequestMethod ::GetCompletionDetails ( args ) = > json! ( {
" id " : id ,
" method " : " getCompletionDetails " ,
" args " : args
} ) ,
2023-08-17 10:46:11 -04:00
RequestMethod ::GetCompletions ( (
specifier ,
position ,
preferences ,
format_code_settings ,
) ) = > {
2021-01-31 22:30:41 -05:00
json! ( {
" id " : id ,
" method " : " getCompletions " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-01-31 22:30:41 -05:00
" position " : position ,
" preferences " : preferences ,
2023-08-17 10:46:11 -04:00
" formatCodeSettings " : format_code_settings ,
2021-01-31 22:30:41 -05:00
} )
}
RequestMethod ::GetDefinition ( ( specifier , position ) ) = > json! ( {
" id " : id ,
" method " : " getDefinition " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-01-31 22:30:41 -05:00
" position " : position ,
} ) ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( specifiers ) = > json! ( {
2020-12-07 05:46:39 -05:00
" id " : id ,
2020-12-29 20:46:58 -05:00
" method " : " getDiagnostics " ,
2021-06-21 17:18:32 -04:00
" specifiers " : specifiers . iter ( ) . map ( | s | state . denormalize_specifier ( s ) ) . collect ::< Vec < String > > ( ) ,
2020-12-07 05:46:39 -05:00
} ) ,
RequestMethod ::GetDocumentHighlights ( (
specifier ,
position ,
files_to_search ,
) ) = > json! ( {
" id " : id ,
" method " : " getDocumentHighlights " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2020-12-07 05:46:39 -05:00
" position " : position ,
" filesToSearch " : files_to_search ,
} ) ,
2021-04-19 21:26:36 -04:00
RequestMethod ::GetEncodedSemanticClassifications ( ( specifier , span ) ) = > {
json! ( {
" id " : id ,
" method " : " getEncodedSemanticClassifications " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-04-19 21:26:36 -04:00
" span " : span ,
} )
}
2021-01-31 22:30:41 -05:00
RequestMethod ::GetImplementation ( ( specifier , position ) ) = > json! ( {
2020-12-07 05:46:39 -05:00
" id " : id ,
2021-01-31 22:30:41 -05:00
" method " : " getImplementation " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2020-12-07 05:46:39 -05:00
" position " : position ,
} ) ,
2023-05-12 19:07:40 -04:00
RequestMethod ::GetNavigateToItems ( GetNavigateToItemsArgs {
2021-11-22 19:08:56 -05:00
search ,
max_result_count ,
file ,
2023-05-12 19:07:40 -04:00
} ) = > json! ( {
2021-11-22 19:08:56 -05:00
" id " : id ,
" method " : " getNavigateToItems " ,
" search " : search ,
" maxResultCount " : max_result_count ,
" file " : file ,
} ) ,
2021-01-31 22:30:41 -05:00
RequestMethod ::GetNavigationTree ( specifier ) = > json! ( {
2020-12-07 05:46:39 -05:00
" id " : id ,
2021-01-31 22:30:41 -05:00
" method " : " getNavigationTree " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-01-31 22:30:41 -05:00
} ) ,
2021-04-02 02:21:07 -04:00
RequestMethod ::GetOutliningSpans ( specifier ) = > json! ( {
" id " : id ,
" method " : " getOutliningSpans " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-04-02 02:21:07 -04:00
} ) ,
2021-01-31 22:30:41 -05:00
RequestMethod ::GetQuickInfo ( ( specifier , position ) ) = > json! ( {
" id " : id ,
" method " : " getQuickInfo " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2020-12-07 05:46:39 -05:00
" position " : position ,
} ) ,
2023-03-30 12:15:21 -04:00
RequestMethod ::FindReferences {
specifier ,
position ,
} = > json! ( {
2021-01-31 22:30:41 -05:00
" id " : id ,
2023-03-30 12:15:21 -04:00
" method " : " findReferences " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-01-31 22:30:41 -05:00
" position " : position ,
2021-01-12 16:53:27 -05:00
} ) ,
2021-02-15 21:34:09 -05:00
RequestMethod ::GetSignatureHelpItems ( ( specifier , position , options ) ) = > {
json! ( {
" id " : id ,
" method " : " getSignatureHelpItems " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-02-15 21:34:09 -05:00
" position " : position ,
" options " : options ,
} )
}
2021-03-23 19:33:25 -04:00
RequestMethod ::GetSmartSelectionRange ( ( specifier , position ) ) = > {
json! ( {
" id " : id ,
" method " : " getSmartSelectionRange " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-03-23 19:33:25 -04:00
" position " : position
} )
}
2021-02-04 13:53:02 -05:00
RequestMethod ::GetSupportedCodeFixes = > json! ( {
" id " : id ,
" method " : " getSupportedCodeFixes " ,
} ) ,
2021-11-22 19:09:19 -05:00
RequestMethod ::GetTypeDefinition {
specifier ,
position ,
} = > json! ( {
" id " : id ,
" method " : " getTypeDefinition " ,
" specifier " : state . denormalize_specifier ( specifier ) ,
" position " : position
} ) ,
2021-04-19 01:11:26 -04:00
RequestMethod ::PrepareCallHierarchy ( ( specifier , position ) ) = > {
json! ( {
" id " : id ,
" method " : " prepareCallHierarchy " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-04-19 01:11:26 -04:00
" position " : position
} )
}
RequestMethod ::ProvideCallHierarchyIncomingCalls ( (
specifier ,
position ,
) ) = > {
json! ( {
" id " : id ,
" method " : " provideCallHierarchyIncomingCalls " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-04-19 01:11:26 -04:00
" position " : position
} )
}
RequestMethod ::ProvideCallHierarchyOutgoingCalls ( (
specifier ,
position ,
) ) = > {
json! ( {
" id " : id ,
" method " : " provideCallHierarchyOutgoingCalls " ,
2021-06-21 17:18:32 -04:00
" specifier " : state . denormalize_specifier ( specifier ) ,
2021-04-19 01:11:26 -04:00
" position " : position
} )
}
2022-10-15 22:39:43 -04:00
RequestMethod ::ProvideInlayHints ( ( specifier , span , preferences ) ) = > {
json! ( {
" id " : id ,
" method " : " provideInlayHints " ,
" specifier " : state . denormalize_specifier ( specifier ) ,
" span " : span ,
" preferences " : preferences ,
} )
}
2022-06-27 13:43:43 -04:00
RequestMethod ::Restart = > json! ( {
" id " : id ,
" method " : " restart " ,
} ) ,
2020-12-07 05:46:39 -05:00
}
}
}
/// Send a request into a runtime and return the JSON value of the response.
2023-05-12 19:07:40 -04:00
fn request (
2020-12-07 05:46:39 -05:00
runtime : & mut JsRuntime ,
2021-11-18 13:50:24 -05:00
state_snapshot : Arc < StateSnapshot > ,
2020-12-07 05:46:39 -05:00
method : RequestMethod ,
2022-02-02 09:25:22 -05:00
token : CancellationToken ,
2020-12-07 05:46:39 -05:00
) -> Result < Value , AnyError > {
2022-01-17 17:09:43 -05:00
let ( performance , request_params ) = {
2020-12-07 05:46:39 -05:00
let op_state = runtime . op_state ( ) ;
let mut op_state = op_state . borrow_mut ( ) ;
let state = op_state . borrow_mut ::< State > ( ) ;
2020-12-21 08:44:26 -05:00
state . state_snapshot = state_snapshot ;
2022-02-02 09:25:22 -05:00
state . token = token ;
2020-12-07 05:46:39 -05:00
state . last_id + = 1 ;
2021-06-21 17:18:32 -04:00
let id = state . last_id ;
2022-01-17 17:09:43 -05:00
( state . performance . clone ( ) , method . to_value ( state , id ) )
2020-12-07 05:46:39 -05:00
} ;
2021-05-11 00:54:10 -04:00
let mark = performance . mark ( " request " , Some ( request_params . clone ( ) ) ) ;
2023-01-27 10:43:16 -05:00
let request_src = format! ( " globalThis.serverRequest( {request_params} ); " ) ;
2023-04-04 08:46:31 -04:00
runtime . execute_script ( located_script_name! ( ) , request_src . into ( ) ) ? ;
2020-12-07 05:46:39 -05:00
let op_state = runtime . op_state ( ) ;
let mut op_state = op_state . borrow_mut ( ) ;
let state = op_state . borrow_mut ::< State > ( ) ;
2021-05-11 00:54:10 -04:00
performance . measure ( mark ) ;
2020-12-07 05:46:39 -05:00
if let Some ( response ) = state . response . clone ( ) {
state . response = None ;
Ok ( response . data )
} else {
Err ( custom_error (
" RequestError " ,
" The response was not received for the request. " ,
) )
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2023-08-01 20:49:09 -04:00
use crate ::cache ::GlobalHttpCache ;
2022-11-28 17:28:54 -05:00
use crate ::cache ::HttpCache ;
2023-08-08 10:23:02 -04:00
use crate ::cache ::RealDenoCacheEnv ;
2021-03-10 05:39:16 -05:00
use crate ::http_util ::HeadersMap ;
2023-07-08 16:06:45 -04:00
use crate ::lsp ::cache ::CacheMetadata ;
2022-10-28 14:48:14 -04:00
use crate ::lsp ::config ::WorkspaceSettings ;
2021-10-28 19:56:01 -04:00
use crate ::lsp ::documents ::Documents ;
2021-06-02 06:29:58 -04:00
use crate ::lsp ::documents ::LanguageId ;
2021-03-15 18:01:41 -04:00
use crate ::lsp ::text ::LineIndex ;
2023-01-14 09:36:19 -05:00
use crate ::tsc ::AssetText ;
use pretty_assertions ::assert_eq ;
2021-03-10 05:39:16 -05:00
use std ::path ::Path ;
use std ::path ::PathBuf ;
2022-04-01 11:15:37 -04:00
use test_util ::TempDir ;
2021-03-10 05:39:16 -05:00
fn mock_state_snapshot (
2021-06-02 06:29:58 -04:00
fixtures : & [ ( & str , & str , i32 , LanguageId ) ] ,
2021-03-10 05:39:16 -05:00
location : & Path ,
) -> StateSnapshot {
2023-08-08 10:23:02 -04:00
let cache = Arc ::new ( GlobalHttpCache ::new (
location . to_path_buf ( ) ,
RealDenoCacheEnv ,
) ) ;
2023-07-08 16:06:45 -04:00
let mut documents = Documents ::new ( cache . clone ( ) ) ;
2021-06-02 06:29:58 -04:00
for ( specifier , source , version , language_id ) in fixtures {
2021-02-17 13:47:18 -05:00
let specifier =
resolve_url ( specifier ) . expect ( " failed to create specifier " ) ;
2021-09-07 10:39:32 -04:00
documents . open (
specifier . clone ( ) ,
* version ,
2022-12-20 15:19:35 -05:00
* language_id ,
2022-05-20 16:40:55 -04:00
( * source ) . into ( ) ,
2021-09-07 10:39:32 -04:00
) ;
2020-12-07 05:46:39 -05:00
}
2020-12-21 08:44:26 -05:00
StateSnapshot {
2021-01-25 18:47:12 -05:00
documents ,
2023-07-08 16:06:45 -04:00
assets : Default ::default ( ) ,
cache_metadata : CacheMetadata ::new ( cache ) ,
2023-09-09 14:37:01 -04:00
config : Default ::default ( ) ,
2023-07-08 16:06:45 -04:00
maybe_import_map : None ,
maybe_node_resolver : None ,
maybe_npm_resolver : None ,
2020-12-07 05:46:39 -05:00
}
}
fn setup (
2022-04-01 11:15:37 -04:00
temp_dir : & TempDir ,
2020-12-07 05:46:39 -05:00
debug : bool ,
config : Value ,
2021-06-02 06:29:58 -04:00
sources : & [ ( & str , & str , i32 , LanguageId ) ] ,
2021-11-18 13:50:24 -05:00
) -> ( JsRuntime , Arc < StateSnapshot > , PathBuf ) {
2023-06-10 11:09:45 -04:00
let location = temp_dir . path ( ) . join ( " deps " ) . to_path_buf ( ) ;
2023-08-08 10:23:02 -04:00
let cache =
Arc ::new ( GlobalHttpCache ::new ( location . clone ( ) , RealDenoCacheEnv ) ) ;
2021-11-18 13:50:24 -05:00
let state_snapshot = Arc ::new ( mock_state_snapshot ( sources , & location ) ) ;
2023-07-08 16:06:45 -04:00
let mut runtime = js_runtime ( Default ::default ( ) , cache ) ;
2023-03-11 11:43:45 -05:00
start ( & mut runtime , debug ) . unwrap ( ) ;
2020-12-07 05:46:39 -05:00
let ts_config = TsConfig ::new ( config ) ;
assert_eq! (
request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot . clone ( ) ,
2022-02-02 09:25:22 -05:00
RequestMethod ::Configure ( ts_config ) ,
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
)
. expect ( " failed request " ) ,
json! ( true )
) ;
2021-03-10 05:39:16 -05:00
( runtime , state_snapshot , location )
2020-12-07 05:46:39 -05:00
}
#[ test ]
fn test_replace_links ( ) {
let actual = replace_links ( r "test {@link http://deno.land/x/mod.ts} test" ) ;
assert_eq! (
actual ,
r "test [http://deno.land/x/mod.ts](http://deno.land/x/mod.ts) test"
) ;
let actual =
replace_links ( r "test {@link http://deno.land/x/mod.ts a link} test" ) ;
assert_eq! ( actual , r "test [a link](http://deno.land/x/mod.ts) test" ) ;
let actual =
replace_links ( r "test {@linkcode http://deno.land/x/mod.ts a link} test" ) ;
assert_eq! ( actual , r "test [`a link`](http://deno.land/x/mod.ts) test" ) ;
}
#[ test ]
fn test_project_configure ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2020-12-07 05:46:39 -05:00
setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ ] ,
2020-12-07 05:46:39 -05:00
) ;
}
#[ test ]
fn test_project_reconfigure ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ ] ,
2020-12-07 05:46:39 -05:00
) ;
let ts_config = TsConfig ::new ( json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" noEmit " : true ,
" lib " : [ " deno.ns " , " deno.worker " ]
} ) ) ;
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2020-12-07 05:46:39 -05:00
RequestMethod ::Configure ( ts_config ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! ( response , json! ( true ) ) ;
}
#[ test ]
2020-12-29 20:46:58 -05:00
fn test_get_diagnostics ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" noEmit " : true ,
} ) ,
2021-06-02 06:29:58 -04:00
& [ (
" file:///a.ts " ,
r # "console.log("hello deno");"# ,
1 ,
LanguageId ::TypeScript ,
) ] ,
2020-12-07 05:46:39 -05:00
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-07 05:46:39 -05:00
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! (
response ,
2021-01-22 05:03:16 -05:00
json! ( {
" file:///a.ts " : [
{
" start " : {
" line " : 0 ,
" character " : 0 ,
} ,
" end " : {
" line " : 0 ,
" character " : 7
} ,
" fileName " : " file:///a.ts " ,
2021-05-27 19:33:11 -04:00
" messageText " : " Cannot find name 'console'. Do you need to change your target library? Try changing the \' lib \' compiler option to include 'dom'. " ,
2021-01-22 05:03:16 -05:00
" sourceLine " : " console.log( \" hello deno \" ); " ,
" category " : 1 ,
" code " : 2584
}
]
} )
2020-12-07 05:46:39 -05:00
) ;
}
2021-02-12 06:49:42 -05:00
#[ test ]
fn test_get_diagnostics_lib ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2021-02-12 06:49:42 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" jsx " : " react " ,
" lib " : [ " esnext " , " dom " , " deno.ns " ] ,
" noEmit " : true ,
} ) ,
2021-06-02 06:29:58 -04:00
& [ (
" file:///a.ts " ,
r # "console.log(document.location);"# ,
1 ,
LanguageId ::TypeScript ,
) ] ,
2021-02-12 06:49:42 -05:00
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2021-02-12 06:49:42 -05:00
let result = request (
& mut runtime ,
state_snapshot ,
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-02-12 06:49:42 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! ( response , json! ( { " file:///a.ts " : [ ] } ) ) ;
}
2020-12-07 05:46:39 -05:00
#[ test ]
fn test_module_resolution ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ (
2020-12-07 05:46:39 -05:00
" file:///a.ts " ,
r #"
import { B } from " https://deno.land/x/b/mod.ts " ;
const b = new B ( ) ;
console . log ( b ) ;
" #,
1 ,
2021-06-02 06:29:58 -04:00
LanguageId ::TypeScript ,
2020-12-07 05:46:39 -05:00
) ] ,
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-07 05:46:39 -05:00
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
2021-01-22 05:03:16 -05:00
assert_eq! ( response , json! ( { " file:///a.ts " : [ ] } ) ) ;
2020-12-07 05:46:39 -05:00
}
#[ test ]
fn test_bad_module_specifiers ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ (
2020-12-07 05:46:39 -05:00
" file:///a.ts " ,
r #"
import { A } from " . " ;
" #,
1 ,
2021-06-02 06:29:58 -04:00
LanguageId ::TypeScript ,
2020-12-07 05:46:39 -05:00
) ] ,
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-07 05:46:39 -05:00
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
2020-12-29 20:46:58 -05:00
assert_eq! (
response ,
2021-01-22 05:03:16 -05:00
json! ( {
" file:///a.ts " : [ {
" start " : {
" line " : 1 ,
" character " : 8
} ,
" end " : {
" line " : 1 ,
" character " : 30
} ,
" fileName " : " file:///a.ts " ,
" messageText " : " \' A \' is declared but its value is never read. " ,
" sourceLine " : " import { A } from \" . \" ; " ,
" category " : 2 ,
" code " : 6133 ,
" reportsUnnecessary " : true ,
} ]
} )
2020-12-29 20:46:58 -05:00
) ;
2020-12-07 05:46:39 -05:00
}
#[ test ]
fn test_remote_modules ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ (
2020-12-07 05:46:39 -05:00
" file:///a.ts " ,
r #"
import { B } from " https://deno.land/x/b/mod.ts " ;
const b = new B ( ) ;
console . log ( b ) ;
" #,
1 ,
2021-06-02 06:29:58 -04:00
LanguageId ::TypeScript ,
2020-12-07 05:46:39 -05:00
) ] ,
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-07 05:46:39 -05:00
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
2021-01-22 05:03:16 -05:00
assert_eq! ( response , json! ( { " file:///a.ts " : [ ] } ) ) ;
2020-12-07 05:46:39 -05:00
}
#[ test ]
fn test_partial_modules ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-07 05:46:39 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-03-10 05:39:16 -05:00
& [ (
2020-12-07 05:46:39 -05:00
" file:///a.ts " ,
r #"
import {
Application ,
Context ,
Router ,
Status ,
} from " https://deno.land/x/oak@v6.3.2/mod.ts " ;
2021-01-29 14:34:33 -05:00
2020-12-07 05:46:39 -05:00
import * as test from
" #,
1 ,
2021-06-02 06:29:58 -04:00
LanguageId ::TypeScript ,
2020-12-07 05:46:39 -05:00
) ] ,
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-07 05:46:39 -05:00
let result = request (
& mut runtime ,
2020-12-21 08:44:26 -05:00
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-07 05:46:39 -05:00
) ;
2020-12-15 14:34:39 -05:00
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! (
response ,
2021-01-22 05:03:16 -05:00
json! ( {
" file:///a.ts " : [ {
" start " : {
" line " : 1 ,
" character " : 8
} ,
" end " : {
" line " : 6 ,
" character " : 55 ,
} ,
" fileName " : " file:///a.ts " ,
" messageText " : " All imports in import declaration are unused. " ,
" sourceLine " : " import { " ,
" category " : 2 ,
" code " : 6192 ,
" reportsUnnecessary " : true
} , {
" start " : {
" line " : 8 ,
" character " : 29
} ,
" end " : {
" line " : 8 ,
" character " : 29
} ,
" fileName " : " file:///a.ts " ,
" messageText " : " Expression expected. " ,
" sourceLine " : " import * as test from " ,
" category " : 1 ,
" code " : 1109
} ]
} )
2020-12-15 14:34:39 -05:00
) ;
}
2020-12-29 20:46:58 -05:00
#[ test ]
fn test_no_debug_failure ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2020-12-29 20:46:58 -05:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-06-02 06:29:58 -04:00
& [ (
" file:///a.ts " ,
r # "const url = new URL("b.js", import."# ,
1 ,
LanguageId ::TypeScript ,
) ] ,
2020-12-29 20:46:58 -05:00
) ;
2021-02-17 13:47:18 -05:00
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
2020-12-29 20:46:58 -05:00
let result = request (
& mut runtime ,
state_snapshot ,
2021-01-22 05:03:16 -05:00
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2020-12-29 20:46:58 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
2021-08-26 20:12:59 -04:00
assert_eq! (
response ,
json! ( {
" file:///a.ts " : [
{
" start " : {
" line " : 0 ,
" character " : 35 ,
} ,
" end " : {
" line " : 0 ,
" character " : 35
} ,
" fileName " : " file:///a.ts " ,
" messageText " : " Identifier expected. " ,
" sourceLine " : " const url = new URL( \" b.js \" , import. " ,
" category " : 1 ,
" code " : 1003 ,
}
]
} )
) ;
2020-12-29 20:46:58 -05:00
}
2020-12-15 14:34:39 -05:00
#[ test ]
2022-04-25 11:23:24 -04:00
fn test_request_assets ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2022-04-25 11:23:24 -04:00
let ( mut runtime , state_snapshot , _ ) =
setup ( & temp_dir , false , json! ( { } ) , & [ ] ) ;
2020-12-21 08:44:26 -05:00
let result = request (
& mut runtime ,
state_snapshot ,
2022-04-25 11:23:24 -04:00
RequestMethod ::GetAssets ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2022-04-25 11:23:24 -04:00
)
. unwrap ( ) ;
2023-01-14 09:36:19 -05:00
let assets : Vec < AssetText > = serde_json ::from_value ( result ) . unwrap ( ) ;
let mut asset_names = assets
. iter ( )
. map ( | a | {
a . specifier
. replace ( " asset:///lib. " , " " )
. replace ( " .d.ts " , " " )
} )
. collect ::< Vec < _ > > ( ) ;
let mut expected_asset_names : Vec < String > = serde_json ::from_str (
include_str! ( concat! ( env! ( " OUT_DIR " ) , " /lib_file_names.json " ) ) ,
)
. unwrap ( ) ;
asset_names . sort ( ) ;
expected_asset_names . sort ( ) ;
2022-04-25 11:23:24 -04:00
// You might have found this assertion starts failing after upgrading TypeScript.
2023-01-14 09:36:19 -05:00
// Ensure build.rs is updated so these match.
assert_eq! ( asset_names , expected_asset_names ) ;
2022-04-25 11:23:24 -04:00
// get some notification when the size of the assets grows
let mut total_size = 0 ;
for asset in assets {
2023-01-14 09:36:19 -05:00
total_size + = asset . text . len ( ) ;
2022-04-25 11:23:24 -04:00
}
assert! ( total_size > 0 ) ;
assert! ( total_size < 2_000_000 ) ; // currently as of TS 4.6, it's 0.7MB
2020-12-07 05:46:39 -05:00
}
2021-03-10 05:39:16 -05:00
#[ test ]
fn test_modify_sources ( ) {
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-10 05:39:16 -05:00
let ( mut runtime , state_snapshot , location ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2021-10-10 17:26:22 -04:00
false ,
2021-03-10 05:39:16 -05:00
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
& [ (
" file:///a.ts " ,
r #"
import * as a from " https://deno.land/x/example/a.ts " ;
if ( a . a = = = " b " ) {
console . log ( " fail " ) ;
}
" #,
1 ,
2021-06-02 06:29:58 -04:00
LanguageId ::TypeScript ,
2021-03-10 05:39:16 -05:00
) ] ,
) ;
2023-08-08 10:23:02 -04:00
let cache = Arc ::new ( GlobalHttpCache ::new ( location , RealDenoCacheEnv ) ) ;
2021-03-10 05:39:16 -05:00
let specifier_dep =
resolve_url ( " https://deno.land/x/example/a.ts " ) . unwrap ( ) ;
cache
. set (
& specifier_dep ,
HeadersMap ::default ( ) ,
b " export const b = \" b \" ; \n " ,
)
. unwrap ( ) ;
let specifier = resolve_url ( " file:///a.ts " ) . unwrap ( ) ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-03-10 05:39:16 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! (
response ,
json! ( {
" file:///a.ts " : [
{
" start " : {
" line " : 2 ,
" character " : 16 ,
} ,
" end " : {
" line " : 2 ,
" character " : 17
} ,
" fileName " : " file:///a.ts " ,
" messageText " : " Property \' a \' does not exist on type \' typeof import( \" https://deno.land/x/example/a \" ) \' . " ,
" sourceLine " : " if (a.a === \" b \" ) { " ,
" code " : 2339 ,
" category " : 1 ,
}
]
} )
) ;
cache
. set (
& specifier_dep ,
HeadersMap ::default ( ) ,
b " export const b = \" b \" ; \n \n export const a = \" b \" ; \n " ,
)
. unwrap ( ) ;
let specifier = resolve_url ( " file:///a.ts " ) . unwrap ( ) ;
let result = request (
& mut runtime ,
state_snapshot ,
RequestMethod ::GetDiagnostics ( vec! [ specifier ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-03-10 05:39:16 -05:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! (
response ,
json! ( {
" file:///a.ts " : [ ]
} )
) ;
}
2021-03-15 18:01:41 -04:00
#[ test ]
fn test_completion_entry_filter_text ( ) {
let fixture = CompletionEntry {
kind : ScriptElementKind ::MemberVariableElement ,
name : " ['foo'] " . to_string ( ) ,
insert_text : Some ( " ['foo'] " . to_string ( ) ) ,
.. Default ::default ( )
} ;
let actual = fixture . get_filter_text ( ) ;
assert_eq! ( actual , Some ( " .foo " . to_string ( ) ) ) ;
2021-09-15 22:07:52 -04:00
let fixture = CompletionEntry {
kind : ScriptElementKind ::MemberVariableElement ,
name : " #abc " . to_string ( ) ,
.. Default ::default ( )
} ;
let actual = fixture . get_filter_text ( ) ;
2022-12-29 16:22:47 -05:00
assert_eq! ( actual , None ) ;
2021-09-15 22:07:52 -04:00
let fixture = CompletionEntry {
kind : ScriptElementKind ::MemberVariableElement ,
name : " #abc " . to_string ( ) ,
insert_text : Some ( " this.#abc " . to_string ( ) ) ,
.. Default ::default ( )
} ;
let actual = fixture . get_filter_text ( ) ;
assert_eq! ( actual , Some ( " abc " . to_string ( ) ) ) ;
2021-03-15 18:01:41 -04:00
}
#[ test ]
fn test_completions ( ) {
let fixture = r #"
import { B } from " https://deno.land/x/b/mod.ts " ;
const b = new B ( ) ;
console .
" #;
let line_index = LineIndex ::new ( fixture ) ;
let position = line_index
. offset_tsc ( lsp ::Position {
line : 5 ,
character : 16 ,
} )
. unwrap ( ) ;
2022-04-01 11:15:37 -04:00
let temp_dir = TempDir ::new ( ) ;
2021-03-15 18:01:41 -04:00
let ( mut runtime , state_snapshot , _ ) = setup (
2022-04-01 11:15:37 -04:00
& temp_dir ,
2021-03-15 18:01:41 -04:00
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
2021-06-02 06:29:58 -04:00
& [ ( " file:///a.ts " , fixture , 1 , LanguageId ::TypeScript ) ] ,
2021-03-15 18:01:41 -04:00
) ;
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetDiagnostics ( vec! [ specifier . clone ( ) ] ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-03-15 18:01:41 -04:00
) ;
assert! ( result . is_ok ( ) ) ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetCompletions ( (
specifier . clone ( ) ,
position ,
GetCompletionsAtPositionOptions {
user_preferences : UserPreferences {
include_completions_with_insert_text : Some ( true ) ,
.. Default ::default ( )
} ,
trigger_character : Some ( " . " . to_string ( ) ) ,
2022-07-11 19:35:18 -04:00
trigger_kind : None ,
2021-03-15 18:01:41 -04:00
} ,
2023-08-17 10:46:11 -04:00
Default ::default ( ) ,
2021-03-15 18:01:41 -04:00
) ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-03-15 18:01:41 -04:00
) ;
assert! ( result . is_ok ( ) ) ;
let response : CompletionInfo =
serde_json ::from_value ( result . unwrap ( ) ) . unwrap ( ) ;
2022-11-21 20:17:14 -05:00
assert_eq! ( response . entries . len ( ) , 22 ) ;
2021-03-15 18:01:41 -04:00
let result = request (
& mut runtime ,
state_snapshot ,
RequestMethod ::GetCompletionDetails ( GetCompletionDetailsArgs {
specifier ,
position ,
name : " log " . to_string ( ) ,
source : None ,
2022-07-11 19:35:18 -04:00
preferences : None ,
2023-08-17 10:46:11 -04:00
format_code_settings : None ,
2021-03-15 18:01:41 -04:00
data : None ,
} ) ,
2022-02-02 09:25:22 -05:00
Default ::default ( ) ,
2021-03-15 18:01:41 -04:00
) ;
assert! ( result . is_ok ( ) ) ;
let response = result . unwrap ( ) ;
assert_eq! (
response ,
json! ( {
" name " : " log " ,
" kindModifiers " : " declare " ,
" kind " : " method " ,
" displayParts " : [
{
" text " : " ( " ,
" kind " : " punctuation "
} ,
{
" text " : " method " ,
" kind " : " text "
} ,
{
" text " : " ) " ,
" kind " : " punctuation "
} ,
{
" text " : " " ,
" kind " : " space "
} ,
{
" text " : " Console " ,
" kind " : " interfaceName "
} ,
{
" text " : " . " ,
" kind " : " punctuation "
} ,
{
" text " : " log " ,
" kind " : " methodName "
} ,
{
" text " : " ( " ,
" kind " : " punctuation "
} ,
{
" text " : " ... " ,
" kind " : " punctuation "
} ,
{
" text " : " data " ,
" kind " : " parameterName "
} ,
{
" text " : " : " ,
" kind " : " punctuation "
} ,
{
" text " : " " ,
" kind " : " space "
} ,
{
" text " : " any " ,
" kind " : " keyword "
} ,
{
" text " : " [ " ,
" kind " : " punctuation "
} ,
{
" text " : " ] " ,
" kind " : " punctuation "
} ,
{
" text " : " ) " ,
" kind " : " punctuation "
} ,
{
" text " : " : " ,
" kind " : " punctuation "
} ,
{
" text " : " " ,
" kind " : " space "
} ,
{
" text " : " void " ,
" kind " : " keyword "
}
] ,
" documentation " : [ ]
} )
) ;
}
2022-07-11 19:35:18 -04:00
2023-08-17 10:46:11 -04:00
#[ test ]
fn test_completions_fmt ( ) {
let fixture_a = r #"
console . log ( someLongVaria )
" #;
let fixture_b = r #"
export const someLongVariable = 1
" #;
let line_index = LineIndex ::new ( fixture_a ) ;
let position = line_index
. offset_tsc ( lsp ::Position {
line : 1 ,
character : 33 ,
} )
. unwrap ( ) ;
let temp_dir = TempDir ::new ( ) ;
let ( mut runtime , state_snapshot , _ ) = setup (
& temp_dir ,
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
& [
( " file:///a.ts " , fixture_a , 1 , LanguageId ::TypeScript ) ,
( " file:///b.ts " , fixture_b , 1 , LanguageId ::TypeScript ) ,
] ,
) ;
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetDiagnostics ( vec! [ specifier . clone ( ) ] ) ,
Default ::default ( ) ,
) ;
assert! ( result . is_ok ( ) ) ;
let fmt_options_config = FmtOptionsConfig {
semi_colons : Some ( false ) ,
2023-09-18 15:48:32 -04:00
single_quote : Some ( true ) ,
2023-08-17 10:46:11 -04:00
.. Default ::default ( )
} ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetCompletions ( (
specifier . clone ( ) ,
position ,
GetCompletionsAtPositionOptions {
user_preferences : UserPreferences {
2023-09-18 15:48:32 -04:00
quote_preference : Some ( ( & fmt_options_config ) . into ( ) ) ,
2023-08-17 10:46:11 -04:00
include_completions_for_module_exports : Some ( true ) ,
include_completions_with_insert_text : Some ( true ) ,
.. Default ::default ( )
} ,
.. Default ::default ( )
} ,
( & fmt_options_config ) . into ( ) ,
) ) ,
Default ::default ( ) ,
)
. unwrap ( ) ;
let info : CompletionInfo = serde_json ::from_value ( result ) . unwrap ( ) ;
let entry = info
. entries
. iter ( )
. find ( | e | & e . name = = " someLongVariable " )
. unwrap ( ) ;
let result = request (
& mut runtime ,
state_snapshot ,
RequestMethod ::GetCompletionDetails ( GetCompletionDetailsArgs {
specifier ,
position ,
name : entry . name . clone ( ) ,
source : entry . source . clone ( ) ,
2023-09-18 15:48:32 -04:00
preferences : Some ( UserPreferences {
quote_preference : Some ( ( & fmt_options_config ) . into ( ) ) ,
.. Default ::default ( )
} ) ,
2023-08-17 10:46:11 -04:00
format_code_settings : Some ( ( & fmt_options_config ) . into ( ) ) ,
data : entry . data . clone ( ) ,
} ) ,
Default ::default ( ) ,
)
. unwrap ( ) ;
let details : CompletionEntryDetails =
serde_json ::from_value ( result ) . unwrap ( ) ;
let actions = details . code_actions . unwrap ( ) ;
let action = actions
. iter ( )
. find ( | a | & a . description = = r # "Add import from "./b.ts""# )
. unwrap ( ) ;
let changes = action . changes . first ( ) . unwrap ( ) ;
let change = changes . text_changes . first ( ) . unwrap ( ) ;
assert_eq! (
change . new_text ,
2023-09-18 15:48:32 -04:00
" import { someLongVariable } from './b.ts' \n "
2023-08-17 10:46:11 -04:00
) ;
}
2023-08-25 20:50:47 -04:00
#[ test ]
fn test_get_edits_for_file_rename ( ) {
let temp_dir = TempDir ::new ( ) ;
let ( mut runtime , state_snapshot , _ ) = setup (
& temp_dir ,
false ,
json! ( {
" target " : " esnext " ,
" module " : " esnext " ,
" lib " : [ " deno.ns " , " deno.window " ] ,
" noEmit " : true ,
} ) ,
& [
(
" file:///a.ts " ,
r # "import "./b.ts";"# ,
1 ,
LanguageId ::TypeScript ,
) ,
( " file:///b.ts " , r # ""# , 1 , LanguageId ::TypeScript ) ,
] ,
) ;
let specifier = resolve_url ( " file:///a.ts " ) . expect ( " could not resolve url " ) ;
let result = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetDiagnostics ( vec! [ specifier . clone ( ) ] ) ,
Default ::default ( ) ,
) ;
assert! ( result . is_ok ( ) ) ;
let changes = request (
& mut runtime ,
state_snapshot . clone ( ) ,
RequestMethod ::GetEditsForFileRename ( (
resolve_url ( " file:///b.ts " ) . unwrap ( ) ,
resolve_url ( " file:///c.ts " ) . unwrap ( ) ,
Default ::default ( ) ,
Default ::default ( ) ,
) ) ,
Default ::default ( ) ,
)
. unwrap ( ) ;
let changes : Vec < FileTextChanges > =
serde_json ::from_value ( changes ) . unwrap ( ) ;
assert_eq! (
changes ,
vec! [ FileTextChanges {
file_name : " file:///a.ts " . to_string ( ) ,
text_changes : vec ! [ TextChange {
span : TextSpan {
start : 8 ,
length : 6 ,
} ,
new_text : " ./c.ts " . to_string ( ) ,
} ] ,
is_new_file : None ,
} ]
) ;
}
2022-10-28 14:48:14 -04:00
#[ test ]
2023-09-25 22:54:07 -04:00
fn include_suppress_inlay_hint_settings ( ) {
2022-10-28 14:48:14 -04:00
let mut settings = WorkspaceSettings ::default ( ) ;
settings
2023-09-21 01:46:39 -04:00
. typescript
2022-10-28 14:48:14 -04:00
. inlay_hints
. parameter_names
. suppress_when_argument_matches_name = true ;
settings
2023-09-21 01:46:39 -04:00
. typescript
2022-10-28 14:48:14 -04:00
. inlay_hints
. variable_types
. suppress_when_type_matches_name = true ;
2023-09-25 22:54:07 -04:00
let mut config = config ::Config ::new ( ) ;
config . set_workspace_settings ( settings ) ;
let user_preferences = UserPreferences ::from_config_for_specifier (
& config ,
& Default ::default ( ) ,
& ModuleSpecifier ::parse ( " file:///foo.ts " ) . unwrap ( ) ,
) ;
2022-10-28 14:48:14 -04:00
assert_eq! (
user_preferences . include_inlay_variable_type_hints_when_type_matches_name ,
Some ( false )
) ;
assert_eq! (
user_preferences
. include_inlay_parameter_name_hints_when_argument_matches_name ,
Some ( false )
) ;
}
2020-12-07 05:46:39 -05:00
}