namespace ts . server { interface StackTraceError extends Error { stack ? : string ; } export interface ServerCancellationToken extends HostCancellationToken { setRequest ( requestId ) : void ; resetRequest ( requestId ) : void ; } export const nullCancellationToken = { isCancellationRequested : ( ) => false , setRequest : ( ) => void 0 , resetRequest : ( ) => void 0 } ; function hrTimeToMilliseconds ( time ) { const seconds = time [ 0 ] ; const nanoseconds = time [ 1 ] ; return ( ( 1e9 * seconds ) + nanoseconds ) / 1000000.0 ; } function isDeclarationFileInJSOnlyNonConfiguredProject ( project , file ) { if ( ( isInferredProject ( project ) || isExternalProject ( project ) ) && project . isJsOnlyProject ( ) ) { const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ; return scriptInfo && ! scriptInfo . isJavaScript ( ) ; } return false ; } function dtsChangeCanAffectEmit ( compilationSettings ) { return getEmitDeclarations ( compilationSettings ) || ! ! compilationSettings . emitDecoratorMetadata ; } function formatDiag ( fileName , project , diag ) { const scriptInfo = project . getScriptInfoForNormalizedPath ( fileName ) ! ; return { start : scriptInfo . positionToLineOffset ( diag . start ! ) , end : scriptInfo . positionToLineOffset ( diag . start ! + diag . length ! ) , text : flattenDiagnosticMessageText ( diag . messageText , "\n" ) , code : diag . code , category : diagnosticCategoryName ( diag ) , reportsUnnecessary : diag . reportsUnnecessary , reportsDeprecated : diag . reportsDeprecated , source : diag . source , relatedInformation : map ( diag . relatedInformation , formatRelatedInformation ) , } ; } function formatRelatedInformation ( info ) { if ( ! info . file ) { return { message : flattenDiagnosticMessageText ( info . messageText , "\n" ) , category : diagnosticCategoryName ( info ) , code : info . code } ; } return { span : { start : convertToLocation ( getLineAndCharacterOfPosition ( info . file , info . start ! ) ) , end : convertToLocation ( getLineAndCharacterOfPosition ( info . file , info . start ! + info . length ! ) ) , file : info . file . fileName } , message : flattenDiagnosticMessageText ( info . messageText , "\n" ) , category : diagnosticCategoryName ( info ) , code : info . code } ; } function convertToLocation ( lineAndCharacter ) { return { line : lineAndCharacter . line + 1 , offset : lineAndCharacter . character + 1 } ; } function formatDiagnosticToProtocol ( diag , includeFileName ) ; function formatDiagnosticToProtocol ( diag , includeFileName ) ; function formatDiagnosticToProtocol ( diag , includeFileName ) { const start = ( diag . file && convertToLocation ( getLineAndCharacterOfPosition ( diag . file , diag . start ! ) ) ) ! ; const end = ( diag . file && convertToLocation ( getLineAndCharacterOfPosition ( diag . file , diag . start ! + diag . length ! ) ) ) ! ; const text = flattenDiagnosticMessageText ( diag . messageText , "\n" ) ; const { code , source } = diag ; const category = diagnosticCategoryName ( diag ) ; const common = { start , end , text , code , category , reportsUnnecessary : diag . reportsUnnecessary , reportsDeprecated : diag . reportsDeprecated , source , relatedInformation : map ( diag . relatedInformation , formatRelatedInformation ) , } ; return includeFileName ? { ... common , fileName : diag . file && diag . file . fileName } : common ; } export interface PendingErrorCheck { fileName : NormalizedPath ; project : Project ; } function allEditsBeforePos ( edits , pos ) { return edits . every ( edit => textSpanEnd ( edit . span ) < pos ) ; } export type CommandNames = protocol . CommandTypes ; export const CommandNames = ( protocol as any ) . CommandTypes ; export function formatMessage < T extends protocol . Message > ( msg , logger , byteLength , newLine ) { const verboseLogging = logger . hasLevel ( LogLevel . verbose ) ; const json = JSON . stringify ( msg ) ; if ( verboseLogging ) { logger . info ( `${ msg . type }:${ indent ( json ) }` ) ; } const len = byteLength ( json , "utf8" ) ; return `Content-Length: ${ 1 + len }\r\n\r\n${ json }${ newLine }` ; } /**
     * Allows to schedule next step in multistep operation
     */ interface NextStep { immediate ( action ) : void ; delay ( ms , action ) : void ; } /**
     * External capabilities used by multistep operation
     */ interface MultistepOperationHost { getCurrentRequestId ( ) : number ; sendRequestCompletedEvent ( requestId ) : void ; getServerHost ( ) : ServerHost ; isCancellationRequested ( ) : boolean ; executeWithRequestId ( requestId , action ) : void ; logError ( error , message ) : void ; } /**
     * Represents operation that can schedule its next step to be executed later.
     * Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed.
     */ class MultistepOperation implements NextStep { private requestId : number | undefined ; private timerHandle : any ; private immediateId : number | undefined ; constructor ( private readonly operationHost ) { } public startNew ( action ) { this . complete ( ) ; this . requestId = this . operationHost . getCurrentRequestId ( ) ; this . executeAction ( action ) ; } private complete ( ) { if ( this . requestId !== undefined ) { this . operationHost . sendRequestCompletedEvent ( this . requestId ) ; this . requestId = undefined ; } this . setTimerHandle ( undefined ) ; this . setImmediateId ( undefined ) ; } public immediate ( action ) { const requestId = this . requestId ! ; Debug . assert ( requestId === this . operationHost . getCurrentRequestId ( ) , "immediate: incorrect request id" ) ; this . setImmediateId ( this . operationHost . getServerHost ( ) . setImmediate ( ( ) => { this . immediateId = undefined ; this . operationHost . executeWithRequestId ( requestId , ( ) => this . executeAction ( action ) ) ; } ) ) ; } public delay ( ms , action ) { const requestId = this . requestId ! ; Debug . assert ( requestId === this . operationHost . getCurrentRequestId ( ) , "delay: incorrect request id" ) ; this . setTimerHandle ( this . operationHost . getServerHost ( ) . setTimeout ( ( ) => { this . timerHandle = undefined ; this . operationHost . executeWithRequestId ( requestId , ( ) => this . executeAction ( action ) ) ; } , ms ) ) ; } private executeAction ( action ) { let stop = false ; try { if ( this . operationHost . isCancellationRequested ( ) ) { stop = true ; tracing ?. instant ( tracing . Phase . Session , "stepCanceled" , { seq : this . requestId , early : true } ) ; } else { tracing ?. push ( tracing . Phase . Session , "stepAction" , { seq : this . requestId } ) ; action ( this ) ; tracing ?. pop ( ) ; } } catch ( e ) { tracing ?. popAll ( ) ; stop = true ; if ( e instanceof OperationCanceledException ) { tracing ?. instant ( tracing . Phase . Session , "stepCanceled" , { seq : this . requestId } ) ; } else { tracing ?. instant ( tracing . Phase . Session , "stepError" , { seq : this . requestId , message : ( e as Error ) . message } ) ; this . operationHost . logError ( e , `delayed processing of request ${ this . requestId }` ) ; } } if ( stop || ! this . hasPendingWork ( ) ) { this . complete ( ) ; } } private setTimerHandle ( timerHandle ) { if ( this . timerHandle !== undefined ) { this . operationHost . getServerHost ( ) . clearTimeout ( this . timerHandle ) ; } this . timerHandle = timerHandle ; } private setImmediateId ( immediateId ) { if ( this . immediateId !== undefined ) { this . operationHost . getServerHost ( ) . clearImmediate ( this . immediateId ) ; } this . immediateId = immediateId ; } private hasPendingWork ( ) { return ! ! this . timerHandle || ! ! this . immediateId ; } } export type Event = < T extends object > ( body : T , eventName : string ) => void ; export interface EventSender { event : Event ; } internal export function toEvent ( eventName , body ) { return { seq : 0 , type : "event" , event : eventName , body } ; } type Projects = readonly Project [ ] | { readonly projects : readonly Project [ ] ; readonly symLinkedProjects : MultiMap < Path , Project > ; } ; /**
     * This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
     */ function combineProjectOutput < T , U > ( defaultValue , getValue , projects , action ) { const outputs = flatMapToMutable ( isArray ( projects ) ? projects : projects . projects , project => action ( project , defaultValue ) ) ; if ( ! isArray ( projects ) && projects . symLinkedProjects ) { projects . symLinkedProjects . forEach ( ( projects , path ) => { const value = getValue ( path ) ; outputs . push ( ... flatMap ( projects , project => action ( project , value ) ) ) ; } ) ; } return deduplicate ( outputs , equateValues ) ; } type CombineOutputResult < T > = { project : Project ; result : readonly T [ ] ; } [ ] ; function combineOutputResultContains < T > ( outputs , output , areEqual ) { return outputs . some ( ( { result } ) => contains ( result , output , areEqual ) ) ; } function addToCombineOutputResult < T > ( outputs , project , result ) { if ( result . length ) outputs . push ( { project , result } ) ; } function combineProjectOutputFromEveryProject < T > ( projectService , action , areEqual ) { const outputs = [ ] ; projectService . loadAncestorProjectTree ( ) ; projectService . forEachEnabledProject ( project => { const theseOutputs = action ( project ) ; addToCombineOutputResult ( outputs , project , filter ( theseOutputs , output => ! combineOutputResultContains ( outputs , output , areEqual ) ) ) ; } ) ; return outputs ; } function flattenCombineOutputResult < T > ( outputs ) { return flatMap ( outputs , ( { result } ) => result ) ; } function combineProjectOutputWhileOpeningReferencedProjects < T > ( projects , defaultProject , action , getLocation , resultsEqual ) { const outputs = [ ] ; combineProjectOutputWorker ( projects , defaultProject , undefined , ( project , _ , tryAddToTodo ) => { const theseOutputs = action ( project ) ; addToCombineOutputResult ( outputs , project , filter ( theseOutputs , output => ! combineOutputResultContains ( outputs , output , resultsEqual ) && ! tryAddToTodo ( project , getLocation ( output ) ) ) ) ; } ) ; return outputs ; } function combineProjectOutputForRenameLocations ( projects , defaultProject , initialLocation , findInStrings , findInComments , hostPreferences ) { const outputs = [ ] ; combineProjectOutputWorker ( projects , defaultProject , initialLocation , ( project , location , tryAddToTodo ) => { for ( const output of project . getLanguageService ( ) . findRenameLocations ( location . fileName , location . pos , findInStrings , findInComments , hostPreferences . providePrefixAndSuffixTextForRename ) || emptyArray ) { if ( ! contains ( outputs , output , documentSpansEqual ) && ! tryAddToTodo ( project , documentSpanLocation ( output ) ) ) { outputs . push ( output ) ; } } } ) ; return outputs ; } function getDefinitionLocation ( defaultProject , initialLocation ) { const infos = defaultProject . getLanguageService ( ) . getDefinitionAtPosition ( initialLocation . fileName , initialLocation . pos ) ; const info = infos && firstOrUndefined ( infos ) ; return info && ! info . isLocal ? { fileName : info . fileName , pos : info . textSpan . start } : undefined ; } function combineProjectOutputForReferences ( projects , defaultProject , initialLocation ) { const outputs = [ ] ; combineProjectOutputWorker ( projects , defaultProject , initialLocation , ( project , location , getMappedLocation ) => { for ( const outputReferencedSymbol of project . getLanguageService ( ) . findReferences ( location . fileName , location . pos ) || emptyArray ) { const mappedDefinitionFile = getMappedLocation ( project , documentSpanLocation ( outputReferencedSymbol . definition ) ) ; const definition = mappedDefinitionFile === undefined ? outputReferencedSymbol . definition : { ... outputReferencedSymbol . definition , textSpan : createTextSpan ( mappedDefinitionFile . pos , outputReferencedSymbol . definition . textSpan . length ) , fileName : mappedDefinitionFile . fileName , contextSpan : getMappedContextSpan ( outputReferencedSymbol . definition , project ) } ; let symbolToAddTo = find ( outputs , o => documentSpansEqual ( o . definition , definition ) ) ; if ( ! symbolToAddTo ) { symbolToAddTo = { definition , references : [ ] } ; outputs . push ( symbolToAddTo ) ; } for ( const ref of outputReferencedSymbol . references ) { if ( ! contains ( symbolToAddTo . references , ref , documentSpansEqual ) && ! getMappedLocation ( project , documentSpanLocation ( ref ) ) ) { symbolToAddTo . references . push ( ref ) ; } } } } ) ; return outputs . filter ( o => o . references . length !== 0 ) ; } function combineProjectOutputForFileReferences ( projects , defaultProject , fileName ) { const outputs = [ ] ; combineProjectOutputWorker ( projects , defaultProject , undefined , project => { for ( const referenceEntry of project . getLanguageService ( ) . getFileReferences ( fileName ) || emptyArray ) { if ( ! contains ( outputs , referenceEntry , documentSpansEqual ) ) { outputs . push ( referenceEntry ) ; } } } ) ; return outputs ; } interface ProjectAndLocation < TLocation extends DocumentPosition | undefined > { readonly project : Project ; readonly location : TLocation ; } function forEachProjectInProjects ( projects , path , cb ) { for ( const project of isArray ( projects ) ? projects : projects . projects ) { cb ( project , path ) ; } if ( ! isArray ( projects ) && projects . symLinkedProjects ) { projects . symLinkedProjects . forEach ( ( symlinkedProjects , symlinkedPath ) => { for ( const project of symlinkedProjects ) { cb ( project , symlinkedPath ) ; } } ) ; } } type CombineProjectOutputCallback < TLocation extends DocumentPosition | undefined > = ( project : Project , location : TLocation , getMappedLocation : ( project : Project , location : DocumentPosition ) => DocumentPosition | undefined ) => void ; function combineProjectOutputWorker < TLocation extends DocumentPosition | undefined > ( projects , defaultProject , initialLocation , cb ) { const projectService = defaultProject . projectService ; let toDo ; const seenProjects = new Set < string > ( ) ; forEachProjectInProjects ( projects , initialLocation && initialLocation . fileName , ( project , path ) => { const location = ( initialLocation ? { fileName : path , pos : initialLocation . pos } : undefined ) as TLocation ; toDo = callbackProjectAndLocation ( project , location , projectService , toDo , seenProjects , cb ) ; } ) ; if ( initialLocation ) { const defaultDefinition = getDefinitionLocation ( defaultProject , initialLocation ) ; if ( defaultDefinition ) { const getGeneratedDefinition = memoize ( ( ) => defaultProject . isSourceOfProjectReferenceRedirect ( defaultDefinition . fileName ) ? defaultDefinition : defaultProject . getLanguageService ( ) . getSourceMapper ( ) . tryGetGeneratedPosition ( defaultDefinition ) ) ; const getSourceDefinition = memoize ( ( ) => defaultProject . isSourceOfProjectReferenceRedirect ( defaultDefinition . fileName ) ? defaultDefinition : defaultProject . getLanguageService ( ) . getSourceMapper ( ) . tryGetSourcePosition ( defaultDefinition ) ) ; projectService . loadAncestorProjectTree ( seenProjects ) ; projectService . forEachEnabledProject ( project => { if ( ! addToSeen ( seenProjects , project ) ) return ; const definition = mapDefinitionInProject ( defaultDefinition , project , getGeneratedDefinition , getSourceDefinition ) ; if ( definition ) { toDo = callbackProjectAndLocation < TLocation > ( project , definition as TLocation , projectService , toDo , seenProjects , cb ) ; } } ) ; } } while ( toDo && toDo . length ) { const next = toDo . pop ( ) ; Debug . assertIsDefined ( next ) ; toDo = callbackProjectAndLocation ( next . project , next . location , projectService , toDo , seenProjects , cb ) ; } } function mapDefinitionInProject ( definition , project , getGeneratedDefinition , getSourceDefinition ) { if ( project . containsFile ( toNormalizedPath ( definition . fileName ) ) && ! isLocationProjectReferenceRedirect ( project , definition ) ) { return definition ; } const generatedDefinition = getGeneratedDefinition ( ) ; if ( generatedDefinition && project . containsFile ( toNormalizedPath ( generatedDefinition . fileName ) ) ) return generatedDefinition ; const sourceDefinition = getSourceDefinition ( ) ; return sourceDefinition && project . containsFile ( toNormalizedPath ( sourceDefinition . fileName ) ) ? sourceDefinition : undefined ; } function isLocationProjectReferenceRedirect ( project , location ) { if ( ! location ) return false ; const program = project . getLanguageService ( ) . getProgram ( ) ; if ( ! program ) return false ; const sourceFile = program . getSourceFile ( location . fileName ) ; return ! ! sourceFile && sourceFile . resolvedPath !== sourceFile . path && sourceFile . resolvedPath !== project . toPath ( location . fileName ) ; } function callbackProjectAndLocation < TLocation extends DocumentPosition | undefined > ( project , location , projectService , toDo , seenProjects , cb ) { if ( project . getCancellationToken ( ) . isCancellationRequested ( ) ) return undefined ; if ( isLocationProjectReferenceRedirect ( project , location ) ) return toDo ; cb ( project , location , ( innerProject , location ) => { addToSeen ( seenProjects , project ) ; const originalLocation = projectService . getOriginalLocationEnsuringConfiguredProject ( innerProject , location ) ; if ( ! originalLocation ) return undefined ; const originalScriptInfo = projectService . getScriptInfo ( originalLocation . fileName ) ! ; toDo = toDo || [ ] ; for ( const project of originalScriptInfo . containingProjects ) { addToTodo ( project , originalLocation as TLocation , toDo , seenProjects ) ; } const symlinkedProjectsMap = projectService . getSymlinkedProjects ( originalScriptInfo ) ; if ( symlinkedProjectsMap ) { symlinkedProjectsMap . forEach ( ( symlinkedProjects , symlinkedPath ) => { for ( const symlinkedProject of symlinkedProjects ) { addToTodo ( symlinkedProject , { fileName : symlinkedPath as string , pos : originalLocation . pos } as TLocation , toDo ! , seenProjects ) ; } } ) ; } return originalLocation === location ? undefined : originalLocation ; } ) ; return toDo ; } function addToTodo < TLocation extends DocumentPosition | undefined > ( project , location , toDo , seenProjects ) { if ( ! project . isOrphan ( ) && addToSeen ( seenProjects , project ) ) toDo . push ( { project , location } ) ; } function addToSeen ( seenProjects , project ) { return tryAddToSet ( seenProjects , getProjectKey ( project ) ) ; } function getProjectKey ( project ) { return isConfiguredProject ( project ) ? project . canonicalConfigFilePath : project . getProjectName ( ) ; } function documentSpanLocation ( { fileName , textSpan } ) { return { fileName , pos : textSpan . start } ; } function getMappedLocation ( location , project ) { const mapsTo = project . getSourceMapper ( ) . tryGetSourcePosition ( location ) ; return mapsTo && project . projectService . fileExists ( toNormalizedPath ( mapsTo . fileName ) ) ? mapsTo : undefined ; } function getMappedDocumentSpan ( documentSpan , project ) { const newPosition = getMappedLocation ( documentSpanLocation ( documentSpan ) , project ) ; if ( ! newPosition ) return undefined ; return { fileName : newPosition . fileName , textSpan : { start : newPosition . pos , length : documentSpan . textSpan . length } , originalFileName : documentSpan . fileName , originalTextSpan : documentSpan . textSpan , contextSpan : getMappedContextSpan ( documentSpan , project ) , originalContextSpan : documentSpan . contextSpan } ; } function getMappedContextSpan ( documentSpan , project ) { const contextSpanStart = documentSpan . contextSpan && getMappedLocation ( { fileName : documentSpan . fileName , pos : documentSpan . contextSpan . start } , project ) ; const contextSpanEnd = documentSpan . contextSpan && getMappedLocation ( { fileName : documentSpan . fileName , pos : documentSpan . contextSpan . start + documentSpan . contextSpan . length } , project ) ; return contextSpanStart && contextSpanEnd ? { start : contextSpanStart . pos , length : contextSpanEnd . pos - contextSpanStart . pos } : undefined ; } const invalidPartialSemanticModeCommands = [ CommandNames . OpenExternalProject , CommandNames . OpenExternalProjects , CommandNames . CloseExternalProject , CommandNames . SynchronizeProjectList , CommandNames . EmitOutput , CommandNames . CompileOnSaveAffectedFileList , CommandNames . CompileOnSaveEmitFile , CommandNames . CompilerOptionsDiagnosticsFull , CommandNames . EncodedSemanticClassificationsFull , CommandNames . SemanticDiagnosticsSync , CommandNames . SuggestionDiagnosticsSync , CommandNames . GeterrForProject , CommandNames . Reload , CommandNames . ReloadProjects , CommandNames . GetCodeFixes , CommandNames . GetCodeFixesFull , CommandNames . GetCombinedCodeFix , CommandNames . GetCombinedCodeFixFull , CommandNames . ApplyCodeActionCommand , CommandNames . GetSupportedCodeFixes , CommandNames . GetApplicableRefactors , CommandNames . GetEditsForRefactor , CommandNames . GetEditsForRefactorFull , CommandNames . OrganizeImports , CommandNames . OrganizeImportsFull , CommandNames . GetEditsForFileRename , CommandNames . GetEditsForFileRenameFull , CommandNames . ConfigurePlugin , CommandNames . PrepareCallHierarchy , CommandNames . ProvideCallHierarchyIncomingCalls , CommandNames . ProvideCallHierarchyOutgoingCalls , ] ; const invalidSyntacticModeCommands = [ ... invalidPartialSemanticModeCommands , CommandNames . Definition , CommandNames . DefinitionFull , CommandNames . DefinitionAndBoundSpan , CommandNames . DefinitionAndBoundSpanFull , CommandNames . TypeDefinition , CommandNames . Implementation , CommandNames . ImplementationFull , CommandNames . References , CommandNames . ReferencesFull , CommandNames . Rename , CommandNames . RenameLocationsFull , CommandNames . RenameInfoFull , CommandNames . Quickinfo , CommandNames . QuickinfoFull , CommandNames . CompletionInfo , CommandNames . Completions , CommandNames . CompletionsFull , CommandNames . CompletionDetails , CommandNames . CompletionDetailsFull , CommandNames . SignatureHelp , CommandNames . SignatureHelpFull , CommandNames . Navto , CommandNames . NavtoFull , CommandNames . Occurrences , CommandNames . DocumentHighlights , CommandNames . DocumentHighlightsFull , ] ; export interface SessionOptions { host : ServerHost ; cancellationToken : ServerCancellationToken ; useSingleInferredProject : boolean ; useInferredProjectPerProjectRoot : boolean ; typingsInstaller : ITypingsInstaller ; byteLength : ( buf : string , encoding ? : string ) => number ; hrtime : ( start ? : number [ ] ) => number [ ] ; logger : Logger ; /**
         * If falsy, all events are suppressed.
         */ canUseEvents : boolean ; eventHandler ? : ProjectServiceEventHandler ; /** Has no effect if eventHandler is also specified. */ suppressDiagnosticEvents ? : boolean ; deprecated syntaxOnly ? : boolean ; serverMode ? : LanguageServiceMode ; throttleWaitMilliseconds ? : number ; noGetErrOnBackgroundUpdate ? : boolean ; globalPlugins ? : readonly string [ ] ; pluginProbeLocations ? : readonly string [ ] ; allowLocalPluginLoads ? : boolean ; typesMapLocation ? : string ; } export class Session < TMessage = string > implements EventSender { private readonly gcTimer : GcTimer ; protected projectService : ProjectService ; private changeSeq = 0 ; private performanceData : protocol . PerformanceData | undefined ; private currentRequestId ! : number ; private errorCheck : MultistepOperation ; protected host : ServerHost ; private readonly cancellationToken : ServerCancellationToken ; protected readonly typingsInstaller : ITypingsInstaller ; protected byteLength : ( buf : string , encoding ? : string ) => number ; private hrtime : ( start ? : number [ ] ) => number [ ] ; protected logger : Logger ; protected canUseEvents : boolean ; private suppressDiagnosticEvents ? : boolean ; private eventHandler : ProjectServiceEventHandler | undefined ; private readonly noGetErrOnBackgroundUpdate ? : boolean ; constructor ( opts ) { this . host = opts . host ; this . cancellationToken = opts . cancellationToken ; this . typingsInstaller = opts . typingsInstaller ; this . byteLength = opts . byteLength ; this . hrtime = opts . hrtime ; this . logger = opts . logger ; this . canUseEvents = opts . canUseEvents ; this . suppressDiagnosticEvents = opts . suppressDiagnosticEvents ; this . noGetErrOnBackgroundUpdate = opts . noGetErrOnBackgroundUpdate ; const { throttleWaitMilliseconds } = opts ; this . eventHandler = this . canUseEvents ? opts . eventHandler || ( event => this . defaultEventHandler ( event ) ) : undefined ; const multistepOperationHost = { executeWithRequestId : ( requestId , action ) => this . executeWithRequestId ( requestId , action ) , getCurrentRequestId : ( ) => this . currentRequestId , getServerHost : ( ) => this . host , logError : ( err , cmd ) => this . logError ( err , cmd ) , sendRequestCompletedEvent : requestId => this . sendRequestCompletedEvent ( requestId ) , isCancellationRequested : ( ) => this . cancellationToken . isCancellationRequested ( ) } ; this . errorCheck = new MultistepOperation ( multistepOperationHost ) ; const settings = { host : this . host , logger : this . logger , cancellationToken : this . cancellationToken , useSingleInferredProject : opts . useSingleInferredProject , useInferredProjectPerProjectRoot : opts . useInferredProjectPerProjectRoot , typingsInstaller : this . typingsInstaller , throttleWaitMilliseconds , eventHandler : this . eventHandler , suppressDiagnosticEvents : this . suppressDiagnosticEvents , globalPlugins : opts . globalPlugins , pluginProbeLocations : opts . pluginProbeLocations , allowLocalPluginLoads : opts . allowLocalPluginLoads , typesMapLocation : opts . typesMapLocation , syntaxOnly : opts . syntaxOnly , serverMode : opts . serverMode , session : this } ; this . projectService = new ProjectService ( settings ) ; this . projectService . setPerformanceEventHandler ( this . performanceEventHandler . bind ( this ) ) ; this . gcTimer = new GcTimer ( this . host , 7000 , this . logger ) ; switch ( this . projectService . serverMode ) { case LanguageServiceMode . Semantic : break ; case LanguageServiceMode . PartialSemantic : invalidPartialSemanticModeCommands . forEach ( commandName => this . handlers . set ( commandName , request => { throw new Error ( `Request: ${ request . command } not allowed in LanguageServiceMode.PartialSemantic` ) ; } ) ) ; break ; case LanguageServiceMode . Syntactic : invalidSyntacticModeCommands . forEach ( commandName => this . handlers . set ( commandName , request => { throw new Error ( `Request: ${ request . command } not allowed in LanguageServiceMode.Syntactic` ) ; } ) ) ; break ; default : Debug . assertNever ( this . projectService . serverMode ) ; } } private sendRequestCompletedEvent ( requestId ) { this . event < protocol . RequestCompletedEventBody > ( { request_seq : requestId } , "requestCompleted" ) ; } private addPerformanceData ( key , value ) { if ( ! this . performanceData ) { this . performanceData = { } ; } this . performanceData [ key ] = ( this . performanceData [ key ] ?? 0 ) + value ; } private performanceEventHandler ( event ) { switch ( event . kind ) { case "UpdateGraph" : this . addPerformanceData ( "updateGraphDurationMs" , event . durationMs ) ; break ; case "CreatePackageJsonAutoImportProvider" : this . addPerformanceData ( "createAutoImportProviderProgramDurationMs" , event . durationMs ) ; break ; } } private defaultEventHandler ( event ) { switch ( event . eventName ) { case ProjectsUpdatedInBackgroundEvent : const { openFiles } = event . data ; this . projectsUpdatedInBackgroundEvent ( openFiles ) ; break ; case ProjectLoadingStartEvent : const { project , reason } = event . data ; this . event < protocol . ProjectLoadingStartEventBody > ( { projectName : project . getProjectName ( ) , reason } , ProjectLoadingStartEvent ) ; break ; case ProjectLoadingFinishEvent : const { project : finishProject } = event . data ; this . event < protocol . ProjectLoadingFinishEventBody > ( { projectName : finishProject . getProjectName ( ) } , ProjectLoadingFinishEvent ) ; break ; case LargeFileReferencedEvent : const { file , fileSize , maxFileSize } = event . data ; this . event < protocol . LargeFileReferencedEventBody > ( { file , fileSize , maxFileSize } , LargeFileReferencedEvent ) ; break ; case ConfigFileDiagEvent : const { triggerFile , configFileName : configFile , diagnostics } = event . data ; const bakedDiags = map ( diagnostics , diagnostic => formatDiagnosticToProtocol ( diagnostic , true ) ) ; this . event < protocol . ConfigFileDiagnosticEventBody > ( { triggerFile , configFile , diagnostics : bakedDiags } , ConfigFileDiagEvent ) ; break ; case ProjectLanguageServiceStateEvent : { const eventName = ProjectLanguageServiceStateEvent ; this . event < protocol . ProjectLanguageServiceStateEventBody > ( { projectName : event . data . project . getProjectName ( ) , languageServiceEnabled : event . data . languageServiceEnabled } , eventName ) ; break ; } case ProjectInfoTelemetryEvent : { const eventName = "telemetry" ; this . event < protocol . TelemetryEventBody > ( { telemetryEventName : event . eventName , payload : event . data , } , eventName ) ; break ; } } } private projectsUpdatedInBackgroundEvent ( openFiles ) { this . projectService . logger . info ( `got projects updated in background, updating diagnostics for ${ openFiles }` ) ; if ( openFiles . length ) { if ( ! this . suppressDiagnosticEvents && ! this . noGetErrOnBackgroundUpdate ) { this . errorCheck . startNew ( next => this . updateErrorCheck ( next , openFiles , 100 , true ) ) ; } this . event < protocol . ProjectsUpdatedInBackgroundEventBody > ( { openFiles } , ProjectsUpdatedInBackgroundEvent ) ; } } public logError ( err , cmd ) { this . logErrorWorker ( err , cmd ) ; } private logErrorWorker ( err , cmd , fileRequest ? ) { let msg = "Exception on executing command " + cmd ; if ( err . message ) { msg += ":\n" + indent ( err . message ) ; if ( ( err as StackTraceError ) . stack ) { msg += "\n" + indent ( ( err as StackTraceError ) . stack ! ) ; } } if ( this . logger . hasLevel ( LogLevel . verbose ) ) { if ( fileRequest ) { try { const { file , project } = this . getFileAndProject ( fileRequest ) ; const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ; if ( scriptInfo ) { const text = getSnapshotText ( scriptInfo . getSnapshot ( ) ) ; msg += `\n\nFile text of ${ fileRequest . file }:${ indent ( text ) }\n` ; } } catch { } } if ( err . ProgramFiles ) { msg += `\n\nProgram files: ${ JSON . stringify ( err . ProgramFiles ) }\n` ; msg += `\n\nProjects::\n` ; let counter = 0 ; const addProjectInfo = ( project ) => { msg += `\nProject '${ project . projectName }' (${ ProjectKind [ project . projectKind ] }) ${ counter }\n` ; msg += project . filesToString ( true ) ; msg += "\n-----------------------------------------------\n" ; counter ++ ; } ; this . projectService . externalProjects . forEach ( addProjectInfo ) ; this . projectService . configuredProjects . forEach ( addProjectInfo ) ; this . projectService . inferredProjects . forEach ( addProjectInfo ) ; } } this . logger . msg ( msg , Msg . Err ) ; } public send ( msg ) { if ( msg . type === "event" && ! this . canUseEvents ) { if ( this . logger . hasLevel ( LogLevel . verbose ) ) { this . logger . info ( `Session does not support events: ignored event: ${ JSON . stringify ( msg ) }` ) ; } return ; } this . writeMessage ( msg ) ; } protected writeMessage ( msg ) { const msgText = formatMessage ( msg , this . logger , this . byteLength , this . host . newLine ) ; perfLogger . logEvent ( `Response message size: ${ msgText . length }` ) ; this . host . write ( msgText ) ; } public event < T extends object > ( body , eventName ) { tracing ?. instant ( tracing . Phase . Session , "event" , { eventName } ) ; this . send ( toEvent ( eventName , body ) ) ; } deprecated public output ( info , cmdName , reqSeq ? , errorMsg ? ) { this . doOutput ( info , cmdName , reqSeq ! , ! errorMsg , errorMsg ) ; } private doOutput ( info , cmdName , reqSeq , success , message ? ) { const res = { seq : 0 , type : "response" , command : cmdName , request_seq : reqSeq , success , performanceData : this . performanceData } ; if ( success ) { let metadata ; if ( isArray ( info ) ) { res . body = info ; metadata = ( info as WithMetadata < readonly any [ ] > ) . metadata ; delete ( info as WithMetadata < readonly any [ ] > ) . metadata ; } else if ( typeof info === "object" ) { if ( ( info as WithMetadata < { } > ) . metadata ) { const { metadata : infoMetadata , ... body } = ( info as WithMetadata < { } > ) ; res . body = body ; metadata = infoMetadata ; } else { res . body = info ; } } else { res . body = info ; } if ( metadata ) res . metadata = metadata ; } else { Debug . assert ( info === undefined ) ; } if ( message ) { res . message = message ; } this . send ( res ) ; } private semanticCheck ( file , project ) { const diags = isDeclarationFileInJSOnlyNonConfiguredProject ( project , file ) ? emptyArray : project . getLanguageService ( ) . getSemanticDiagnostics ( file ) . filter ( d => ! ! d . file ) ; this . sendDiagnosticsEvent ( file , project , diags , "semanticDiag" ) ; } private syntacticCheck ( file , project ) { this . sendDiagnosticsEvent ( file , project , project . getLanguageService ( ) . getSyntacticDiagnostics ( file ) , "syntaxDiag" ) ; } private suggestionCheck ( file , project ) { this . sendDiagnosticsEvent ( file , project , project . getLanguageService ( ) . getSuggestionDiagnostics ( file ) , "suggestionDiag" ) ; } private sendDiagnosticsEvent ( file , project , diagnostics , kind ) { try { this . event < protocol . DiagnosticEventBody > ( { file , diagnostics : diagnostics . map ( diag => formatDiag ( file , project , diag ) ) } , kind ) ; } catch ( err ) { this . logError ( err , kind ) ; } } /** It is the caller's responsibility to verify that `!this.suppressDiagnosticEvents`. */ private updateErrorCheck ( next , checkList , ms , requireOpen = true ) { Debug . assert ( ! this . suppressDiagnosticEvents ) ; const seq = this . changeSeq ; const followMs = Math . min ( ms , 200 ) ; let index = 0 ; const goNext = ( ) => { index ++ ; if ( checkList . length > index ) { next . delay ( followMs , checkOne ) ; } } ; const checkOne = ( ) => { if ( this . changeSeq !== seq ) { return ; } let item = checkList [ index ] ; if ( isString ( item ) ) { item = this . toPendingErrorCheck ( item ) ; if ( ! item ) { goNext ( ) ; return ; } } const { fileName , project } = item ; updateProjectIfDirty ( project ) ; if ( ! project . containsFile ( fileName , requireOpen ) ) { return ; } this . syntacticCheck ( fileName , project ) ; if ( this . changeSeq !== seq ) { return ; } if ( project . projectService . serverMode !== LanguageServiceMode . Semantic ) { goNext ( ) ; return ; } next . immediate ( ( ) => { this . semanticCheck ( fileName , project ) ; if ( this . changeSeq !== seq ) { return ; } if ( this . getPreferences ( fileName ) . disableSuggestions ) { goNext ( ) ; return ; } next . immediate ( ( ) => { this . suggestionCheck ( fileName , project ) ; goNext ( ) ; } ) ; } ) ; } ; if ( checkList . length > index && this . changeSeq === seq ) { next . delay ( ms , checkOne ) ; } } private cleanProjects ( caption , projects ) { if ( ! projects ) { return ; } this . logger . info ( `cleaning ${ caption }` ) ; for ( const p of projects ) { p . getLanguageService ( false ) . cleanupSemanticCache ( ) ; } } private cleanup ( ) { this . cleanProjects ( "inferred projects" , this . projectService . inferredProjects ) ; this . cleanProjects ( "configured projects" , arrayFrom ( this . projectService . configuredProjects . values ( ) ) ) ; this . cleanProjects ( "external projects" , this . projectService . externalProjects ) ; if ( this . host . gc ) { this . logger . info ( `host.gc()` ) ; this . host . gc ( ) ; } } private getEncodedSyntacticClassifications ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; return languageService . getEncodedSyntacticClassifications ( file , args ) ; } private getEncodedSemanticClassifications ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const format = args . format === "2020" ? SemanticClassificationFormat . TwentyTwenty : SemanticClassificationFormat . Original ; return project . getLanguageService ( ) . getEncodedSemanticClassifications ( file , args , format ) ; } private getProject ( projectFileName ) { return projectFileName === undefined ? undefined : this . projectService . findProject ( projectFileName ) ; } private getConfigFileAndProject ( args ) { const project = this . getProject ( args . projectFileName ) ; const file = toNormalizedPath ( args . file ) ; return { configFile : project && project . hasConfigFile ( file ) ? file : undefined , project } ; } private getConfigFileDiagnostics ( configFile , project , includeLinePosition ) { const projectErrors = project . getAllProjectErrors ( ) ; const optionsErrors = project . getLanguageService ( ) . getCompilerOptionsDiagnostics ( ) ; const diagnosticsForConfigFile = filter ( concatenate ( projectErrors , optionsErrors ) , diagnostic => ! ! diagnostic . file && diagnostic . file . fileName === configFile ) ; return includeLinePosition ? this . convertToDiagnosticsWithLinePositionFromDiagnosticFile ( diagnosticsForConfigFile ) : map ( diagnosticsForConfigFile , diagnostic => formatDiagnosticToProtocol ( diagnostic , false ) ) ; } private convertToDiagnosticsWithLinePositionFromDiagnosticFile ( diagnostics ) { return diagnostics . map < protocol . DiagnosticWithLinePosition > ( d => ( { message : flattenDiagnosticMessageText ( d . messageText , this . host . newLine ) , start : d . start ! , length : d . length ! , category : diagnosticCategoryName ( d ) , code : d . code , source : d . source , startLocation : ( d . file && convertToLocation ( getLineAndCharacterOfPosition ( d . file , d . start ! ) ) ) ! , endLocation : ( d . file && convertToLocation ( getLineAndCharacterOfPosition ( d . file , d . start ! + d . length ! ) ) ) ! , reportsUnnecessary : d . reportsUnnecessary , reportsDeprecated : d . reportsDeprecated , relatedInformation : map ( d . relatedInformation , formatRelatedInformation ) } ) ) ; } private getCompilerOptionsDiagnostics ( args ) { const project = this . getProject ( args . projectFileName ) ! ; return this . convertToDiagnosticsWithLinePosition ( filter ( project . getLanguageService ( ) . getCompilerOptionsDiagnostics ( ) , diagnostic => ! diagnostic . file ) , undefined ) ; } private convertToDiagnosticsWithLinePosition ( diagnostics , scriptInfo ) { return diagnostics . map ( d => ( { message : flattenDiagnosticMessageText ( d . messageText , this . host . newLine ) , start : d . start , length : d . length , category : diagnosticCategoryName ( d ) , code : d . code , source : d . source , startLocation : scriptInfo && scriptInfo . positionToLineOffset ( d . start ! ) , endLocation : scriptInfo && scriptInfo . positionToLineOffset ( d . start ! + d . length ! ) , reportsUnnecessary : d . reportsUnnecessary , reportsDeprecated : d . reportsDeprecated , relatedInformation : map ( d . relatedInformation , formatRelatedInformation ) , } ) as protocol . DiagnosticWithLinePosition ) ; } private getDiagnosticsWorker ( args , isSemantic , selector , includeLinePosition ) { const { project , file } = this . getFileAndProject ( args ) ; if ( isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject ( project , file ) ) { return emptyArray ; } const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ; const diagnostics = selector ( project , file ) ; return includeLinePosition ? this . convertToDiagnosticsWithLinePosition ( diagnostics , scriptInfo ) : diagnostics . map ( d => formatDiag ( file , project , d ) ) ; } private getDefinition ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const definitions = this . mapDefinitionInfoLocations ( project . getLanguageService ( ) . getDefinitionAtPosition ( file , position ) || emptyArray , project ) ; return simplifiedResult ? this . mapDefinitionInfo ( definitions , project ) : definitions . map ( Session . mapToOriginalLocation ) ; } private mapDefinitionInfoLocations ( definitions , project ) { return definitions . map ( ( info ) : DefinitionInfo => { const newDocumentSpan = getMappedDocumentSpan ( info , project ) ; return ! newDocumentSpan ? info : { ... newDocumentSpan , containerKind : info . containerKind , containerName : info . containerName , kind : info . kind , name : info . name , ... info . unverified && { unverified : info . unverified } , } ; } ) ; } private getDefinitionAndBoundSpan ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const scriptInfo = Debug . checkDefined ( project . getScriptInfo ( file ) ) ; const unmappedDefinitionAndBoundSpan = project . getLanguageService ( ) . getDefinitionAndBoundSpan ( file , position ) ; if ( ! unmappedDefinitionAndBoundSpan || ! unmappedDefinitionAndBoundSpan . definitions ) { return { definitions : emptyArray , textSpan : undefined ! } ; } const definitions = this . mapDefinitionInfoLocations ( unmappedDefinitionAndBoundSpan . definitions , project ) ; const { textSpan } = unmappedDefinitionAndBoundSpan ; if ( simplifiedResult ) { return { definitions : this . mapDefinitionInfo ( definitions , project ) , textSpan : toProtocolTextSpan ( textSpan , scriptInfo ) } ; } return { definitions : definitions . map ( Session . mapToOriginalLocation ) , textSpan , } ; } private getEmitOutput ( args ) { const { file , project } = this . getFileAndProject ( args ) ; if ( ! project . shouldEmitFile ( project . getScriptInfo ( file ) ) ) { return { emitSkipped : true , outputFiles : [ ] , diagnostics : [ ] } ; } const result = project . getLanguageService ( ) . getEmitOutput ( file ) ; return args . richResponse ? { ... result , diagnostics : args . includeLinePosition ? this . convertToDiagnosticsWithLinePositionFromDiagnosticFile ( result . diagnostics ) : result . diagnostics . map ( d => formatDiagnosticToProtocol ( d , true ) ) } : result ; } private mapJSDocTagInfo ( tags , project , richResponse ) { return tags ? tags . map ( tag => ( { ... tag , text : richResponse ? this . mapDisplayParts ( tag . text , project ) : tag . text ?. map ( part => part . text ) . join ( "" ) } ) ) : [ ] ; } private mapDisplayParts ( parts , project ) { if ( ! parts ) { return [ ] ; } return parts . map ( part => part . kind !== "linkName" ? part : { ... part , target : this . toFileSpan ( ( part as JSDocLinkDisplayPart ) . target . fileName , ( part as JSDocLinkDisplayPart ) . target . textSpan , project ) , } ) ; } private mapSignatureHelpItems ( items , project , richResponse ) { return items . map ( item => ( { ... item , documentation : this . mapDisplayParts ( item . documentation , project ) , parameters : item . parameters . map ( p => ( { ... p , documentation : this . mapDisplayParts ( p . documentation , project ) } ) ) , tags : this . mapJSDocTagInfo ( item . tags , project , richResponse ) , } ) ) ; } private mapDefinitionInfo ( definitions , project ) { return definitions . map ( def => ( { ... this . toFileSpanWithContext ( def . fileName , def . textSpan , def . contextSpan , project ) , ... def . unverified && { unverified : def . unverified } } ) ) ; } private static mapToOriginalLocation < T extends DocumentSpan > ( def ) { if ( def . originalFileName ) { Debug . assert ( def . originalTextSpan !== undefined , "originalTextSpan should be present if originalFileName is" ) ; return { ... def as any , fileName : def . originalFileName , textSpan : def . originalTextSpan , targetFileName : def . fileName , targetTextSpan : def . textSpan , contextSpan : def . originalContextSpan , targetContextSpan : def . contextSpan } ; } return def ; } private toFileSpan ( fileName , textSpan , project ) { const ls = project . getLanguageService ( ) ; const start = ls . toLineColumnOffset ! ( fileName , textSpan . start ) ; const end = ls . toLineColumnOffset ! ( fileName , textSpanEnd ( textSpan ) ) ; return { file : fileName , start : { line : start . line + 1 , offset : start . character + 1 } , end : { line : end . line + 1 , offset : end . character + 1 } } ; } private toFileSpanWithContext ( fileName , textSpan , contextSpan , project ) { const fileSpan = this . toFileSpan ( fileName , textSpan , project ) ; const context = contextSpan && this . toFileSpan ( fileName , contextSpan , project ) ; return context ? { ... fileSpan , contextStart : context . start , contextEnd : context . end } : fileSpan ; } private getTypeDefinition ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const definitions = this . mapDefinitionInfoLocations ( project . getLanguageService ( ) . getTypeDefinitionAtPosition ( file , position ) || emptyArray , project ) ; return this . mapDefinitionInfo ( definitions , project ) ; } private mapImplementationLocations ( implementations , project ) { return implementations . map ( ( info ) : ImplementationLocation => { const newDocumentSpan = getMappedDocumentSpan ( info , project ) ; return ! newDocumentSpan ? info : { ... newDocumentSpan , kind : info . kind , displayParts : info . displayParts , } ; } ) ; } private getImplementation ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const implementations = this . mapImplementationLocations ( project . getLanguageService ( ) . getImplementationAtPosition ( file , position ) || emptyArray , project ) ; return simplifiedResult ? implementations . map ( ( { fileName , textSpan , contextSpan } ) => this . toFileSpanWithContext ( fileName , textSpan , contextSpan , project ) ) : implementations . map ( Session . mapToOriginalLocation ) ; } private getOccurrences ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const occurrences = project . getLanguageService ( ) . getOccurrencesAtPosition ( file , position ) ; return occurrences ? occurrences . map < protocol . OccurrencesResponseItem > ( occurrence => { const { fileName , isWriteAccess , textSpan , isInString , contextSpan } = occurrence ; const scriptInfo = project . getScriptInfo ( fileName ) ! ; return { ... toProtocolTextSpanWithContext ( textSpan , contextSpan , scriptInfo ) , file : fileName , isWriteAccess , ... ( isInString ? { isInString } : undefined ) } ; } ) : emptyArray ; } private getSyntacticDiagnosticsSync ( args ) { const { configFile } = this . getConfigFileAndProject ( args ) ; if ( configFile ) { return emptyArray ; } return this . getDiagnosticsWorker ( args , false , ( project , file ) => project . getLanguageService ( ) . getSyntacticDiagnostics ( file ) , ! ! args . includeLinePosition ) ; } private getSemanticDiagnosticsSync ( args ) { const { configFile , project } = this . getConfigFileAndProject ( args ) ; if ( configFile ) { return this . getConfigFileDiagnostics ( configFile , project ! , ! ! args . includeLinePosition ) ; } return this . getDiagnosticsWorker ( args , true , ( project , file ) => project . getLanguageService ( ) . getSemanticDiagnostics ( file ) . filter ( d => ! ! d . file ) , ! ! args . includeLinePosition ) ; } private getSuggestionDiagnosticsSync ( args ) { const { configFile } = this . getConfigFileAndProject ( args ) ; if ( configFile ) { return emptyArray ; } return this . getDiagnosticsWorker ( args , true , ( project , file ) => project . getLanguageService ( ) . getSuggestionDiagnostics ( file ) , ! ! args . includeLinePosition ) ; } private getJsxClosingTag ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; const tag = languageService . getJsxClosingTagAtPosition ( file , position ) ; return tag === undefined ? undefined : { newText : tag . newText , caretOffset : 0 } ; } private getDocumentHighlights ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; const documentHighlights = project . getLanguageService ( ) . getDocumentHighlights ( file , position , args . filesToSearch ) ; if ( ! documentHighlights ) return emptyArray ; if ( ! simplifiedResult ) return documentHighlights ; return documentHighlights . map < protocol . DocumentHighlightsItem > ( ( { fileName , highlightSpans } ) => { const scriptInfo = project . getScriptInfo ( fileName ) ! ; return { file : fileName , highlightSpans : highlightSpans . map ( ( { textSpan , kind , contextSpan } ) => ( { ... toProtocolTextSpanWithContext ( textSpan , contextSpan , scriptInfo ) , kind } ) ) } ; } ) ; } private provideInlayHints ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const hints = project . getLanguageService ( ) . provideInlayHints ( file , args , this . getPreferences ( file ) ) ; return hints . map ( hint => ( { ... hint , position : scriptInfo . positionToLineOffset ( hint . position ) , } ) ) ; } private setCompilerOptionsForInferredProjects ( args ) { this . projectService . setCompilerOptionsForInferredProjects ( args . options , args . projectRootPath ) ; } private getProjectInfo ( args ) { return this . getProjectInfoWorker ( args . file , args . projectFileName , args . needFileNameList , false ) ; } private getProjectInfoWorker ( uncheckedFileName , projectFileName , needFileNameList , excludeConfigFiles ) { const { project } = this . getFileAndProjectWorker ( uncheckedFileName , projectFileName ) ; updateProjectIfDirty ( project ) ; const projectInfo = { configFileName : project . getProjectName ( ) , languageServiceDisabled : ! project . languageServiceEnabled , fileNames : needFileNameList ? project . getFileNames ( false , excludeConfigFiles ) : undefined } ; return projectInfo ; } private getRenameInfo ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const position = this . getPositionInFile ( args , file ) ; return project . getLanguageService ( ) . getRenameInfo ( file , position , { allowRenameOfImportPath : this . getPreferences ( file ) . allowRenameOfImportPath } ) ; } private getProjects ( args , getScriptInfoEnsuringProjectsUptoDate ? , ignoreNoProjectError ? ) { let projects ; let symLinkedProjects ; if ( args . projectFileName ) { const project = this . getProject ( args . projectFileName ) ; if ( project ) { projects = [ project ] ; } } else { const scriptInfo = getScriptInfoEnsuringProjectsUptoDate ? this . projectService . getScriptInfoEnsuringProjectsUptoDate ( args . file ) : this . projectService . getScriptInfo ( args . file ) ; if ( ! scriptInfo ) { if ( ignoreNoProjectError ) return emptyArray ; this . projectService . logErrorForScriptInfoNotFound ( args . file ) ; return Errors . ThrowNoProject ( ) ; } projects = scriptInfo . containingProjects ; symLinkedProjects = this . projectService . getSymlinkedProjects ( scriptInfo ) ; } projects = filter ( projects , p => p . languageServiceEnabled && ! p . isOrphan ( ) ) ; if ( ! ignoreNoProjectError && ( ! projects || ! projects . length ) && ! symLinkedProjects ) { this . projectService . logErrorForScriptInfoNotFound ( args . file ?? args . projectFileName ) ; return Errors . ThrowNoProject ( ) ; } return symLinkedProjects ? { projects : projects ! , symLinkedProjects } : projects ! ; } private getDefaultProject ( args ) { if ( args . projectFileName ) { const project = this . getProject ( args . projectFileName ) ; if ( project ) { return project ; } if ( ! args . file ) { return Errors . ThrowNoProject ( ) ; } } const info = this . projectService . getScriptInfo ( args . file ) ! ; return info . getDefaultProject ( ) ; } private getRenameLocations ( args , simplifiedResult ) { const file = toNormalizedPath ( args . file ) ; const position = this . getPositionInFile ( args , file ) ; const projects = this . getProjects ( args ) ; const defaultProject = this . getDefaultProject ( args ) ; const renameInfo = this . mapRenameInfo ( defaultProject . getLanguageService ( ) . getRenameInfo ( file , position , { allowRenameOfImportPath : this . getPreferences ( file ) . allowRenameOfImportPath } ) , Debug . checkDefined ( this . projectService . getScriptInfo ( file ) ) ) ; if ( ! renameInfo . canRename ) return simplifiedResult ? { info : renameInfo , locs : [ ] } : [ ] ; const locations = combineProjectOutputForRenameLocations ( projects , defaultProject , { fileName : args . file , pos : position } , ! ! args . findInStrings , ! ! args . findInComments , this . getPreferences ( file ) ) ; if ( ! simplifiedResult ) return locations ; return { info : renameInfo , locs : this . toSpanGroups ( locations ) } ; } private mapRenameInfo ( info , scriptInfo ) { if ( info . canRename ) { const { canRename , fileToRename , displayName , fullDisplayName , kind , kindModifiers , triggerSpan } = info ; return identity < protocol . RenameInfoSuccess > ( { canRename , fileToRename , displayName , fullDisplayName , kind , kindModifiers , triggerSpan : toProtocolTextSpan ( triggerSpan , scriptInfo ) } ) ; } else { return info ; } } private toSpanGroups ( locations ) { const map = new Map < string , protocol . SpanGroup > ( ) ; for ( const { fileName , textSpan , contextSpan , originalContextSpan : _2 , originalTextSpan : _ , originalFileName : _1 , ... prefixSuffixText } of locations ) { let group = map . get ( fileName ) ; if ( ! group ) map . set ( fileName , group = { file : fileName , locs : [ ] } ) ; const scriptInfo = Debug . checkDefined ( this . projectService . getScriptInfo ( fileName ) ) ; group . locs . push ( { ... toProtocolTextSpanWithContext ( textSpan , contextSpan , scriptInfo ) , ... prefixSuffixText } ) ; } return arrayFrom ( map . values ( ) ) ; } private getReferences ( args , simplifiedResult ) { const file = toNormalizedPath ( args . file ) ; const projects = this . getProjects ( args ) ; const position = this . getPositionInFile ( args , file ) ; const references = combineProjectOutputForReferences ( projects , this . getDefaultProject ( args ) , { fileName : args . file , pos : position } ) ; if ( ! simplifiedResult ) return references ; const defaultProject = this . getDefaultProject ( args ) ; const scriptInfo = defaultProject . getScriptInfoForNormalizedPath ( file ) ! ; const nameInfo = defaultProject . getLanguageService ( ) . getQuickInfoAtPosition ( file , position ) ; const symbolDisplayString = nameInfo ? displayPartsToString ( nameInfo . displayParts ) : "" ; const nameSpan = nameInfo && nameInfo . textSpan ; const symbolStartOffset = nameSpan ? scriptInfo . positionToLineOffset ( nameSpan . start ) . offset : 0 ; const symbolName = nameSpan ? scriptInfo . getSnapshot ( ) . getText ( nameSpan . start , textSpanEnd ( nameSpan ) ) : "" ; const refs = flatMap ( references , referencedSymbol => { return referencedSymbol . references . map ( entry => referenceEntryToReferencesResponseItem ( this . projectService , entry ) ) ; } ) ; return { refs , symbolName , symbolStartOffset , symbolDisplayString } ; } private getFileReferences ( args , simplifiedResult ) { const projects = this . getProjects ( args ) ; const references = combineProjectOutputForFileReferences ( projects , this . getDefaultProject ( args ) , args . file ) ; if ( ! simplifiedResult ) return references ; const refs = references . map ( entry => referenceEntryToReferencesResponseItem ( this . projectService , entry ) ) ; return { refs , symbolName : `"${ args . file }"` } ; } param fileName param fileContent private openClientFile ( fileName , fileContent ? , scriptKind ? , projectRootPath ? ) { this . projectService . openClientFileWithNormalizedPath ( fileName , fileContent , scriptKind , false , projectRootPath ) ; } private getPosition ( args , scriptInfo ) { return args . position !== undefined ? args . position : scriptInfo . lineOffsetToPosition ( args . line , args . offset ) ; } private getPositionInFile ( args , file ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return this . getPosition ( args , scriptInfo ) ; } private getFileAndProject ( args ) { return this . getFileAndProjectWorker ( args . file , args . projectFileName ) ; } private getFileAndLanguageServiceForSyntacticOperation ( args ) { const file = toNormalizedPath ( args . file ) ; const project = this . getProject ( args . projectFileName ) || this . projectService . tryGetDefaultProjectForFile ( file ) ; if ( ! project ) { return Errors . ThrowNoProject ( ) ; } return { file , languageService : project . getLanguageService ( false ) } ; } private getFileAndProjectWorker ( uncheckedFileName , projectFileName ) { const file = toNormalizedPath ( uncheckedFileName ) ; const project = this . getProject ( projectFileName ) || this . projectService . ensureDefaultProjectForFile ( file ) ; return { file , project } ; } private getOutliningSpans ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const spans = languageService . getOutliningSpans ( file ) ; if ( simplifiedResult ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return spans . map ( s => ( { textSpan : toProtocolTextSpan ( s . textSpan , scriptInfo ) , hintSpan : toProtocolTextSpan ( s . hintSpan , scriptInfo ) , bannerText : s . bannerText , autoCollapse : s . autoCollapse , kind : s . kind } ) ) ; } else { return spans ; } } private getTodoComments ( args ) { const { file , project } = this . getFileAndProject ( args ) ; return project . getLanguageService ( ) . getTodoComments ( file , args . descriptors ) ; } private getDocCommentTemplate ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; return languageService . getDocCommentTemplateAtPosition ( file , position , this . getPreferences ( file ) ) ; } private getSpanOfEnclosingComment ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const onlyMultiLine = args . onlyMultiLine ; const position = this . getPositionInFile ( args , file ) ; return languageService . getSpanOfEnclosingComment ( file , position , onlyMultiLine ) ; } private getIndentation ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; const options = args . options ? convertFormatOptions ( args . options ) : this . getFormatOptions ( file ) ; const indentation = languageService . getIndentationAtPosition ( file , position , options ) ; return { position , indentation } ; } private getBreakpointStatement ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; return languageService . getBreakpointStatementAtPosition ( file , position ) ; } private getNameOrDottedNameSpan ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; return languageService . getNameOrDottedNameSpan ( file , position , position ) ; } private isValidBraceCompletion ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const position = this . getPositionInFile ( args , file ) ; return languageService . isValidBraceCompletionAtPosition ( file , position , args . openingBrace . charCodeAt ( 0 ) ) ; } private getQuickInfoWorker ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const quickInfo = project . getLanguageService ( ) . getQuickInfoAtPosition ( file , this . getPosition ( args , scriptInfo ) ) ; if ( ! quickInfo ) { return undefined ; } const useDisplayParts = ! ! this . getPreferences ( file ) . displayPartsForJSDoc ; if ( simplifiedResult ) { const displayString = displayPartsToString ( quickInfo . displayParts ) ; return { kind : quickInfo . kind , kindModifiers : quickInfo . kindModifiers , start : scriptInfo . positionToLineOffset ( quickInfo . textSpan . start ) , end : scriptInfo . positionToLineOffset ( textSpanEnd ( quickInfo . textSpan ) ) , displayString , documentation : useDisplayParts ? this . mapDisplayParts ( quickInfo . documentation , project ) : displayPartsToString ( quickInfo . documentation ) , tags : this . mapJSDocTagInfo ( quickInfo . tags , project , useDisplayParts ) , } ; } else { return useDisplayParts ? quickInfo : { ... quickInfo , tags : this . mapJSDocTagInfo ( quickInfo . tags , project , false ) as JSDocTagInfo [ ] } ; } } private getFormattingEditsForRange ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const startPosition = scriptInfo . lineOffsetToPosition ( args . line , args . offset ) ; const endPosition = scriptInfo . lineOffsetToPosition ( args . endLine , args . endOffset ) ; const edits = languageService . getFormattingEditsForRange ( file , startPosition , endPosition , this . getFormatOptions ( file ) ) ; if ( ! edits ) { return undefined ; } return edits . map ( edit => this . convertTextChangeToCodeEdit ( edit , scriptInfo ) ) ; } private getFormattingEditsForRangeFull ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const options = args . options ? convertFormatOptions ( args . options ) : this . getFormatOptions ( file ) ; return languageService . getFormattingEditsForRange ( file , args . position ! , args . endPosition ! , options ) ; } private getFormattingEditsForDocumentFull ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const options = args . options ? convertFormatOptions ( args . options ) : this . getFormatOptions ( file ) ; return languageService . getFormattingEditsForDocument ( file , options ) ; } private getFormattingEditsAfterKeystrokeFull ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const options = args . options ? convertFormatOptions ( args . options ) : this . getFormatOptions ( file ) ; return languageService . getFormattingEditsAfterKeystroke ( file , args . position ! , args . key , options ) ; } private getFormattingEditsAfterKeystroke ( args ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const position = scriptInfo . lineOffsetToPosition ( args . line , args . offset ) ; const formatOptions = this . getFormatOptions ( file ) ; const edits = languageService . getFormattingEditsAfterKeystroke ( file , position , args . key , formatOptions ) ; if ( ( args . key === "\n" ) && ( ( ! edits ) || ( edits . length === 0 ) || allEditsBeforePos ( edits , position ) ) ) { const { lineText , absolutePosition } = scriptInfo . getAbsolutePositionAndLineText ( args . line ) ; if ( lineText && lineText . search ( "\\S" ) < 0 ) { const preferredIndent = languageService . getIndentationAtPosition ( file , position , formatOptions ) ; let hasIndent = 0 ; let i , len ; for ( i = 0 , len = lineText . length ; i < len ; i ++ ) { if ( lineText . charAt ( i ) === " " ) { hasIndent ++ ; } else if ( lineText . charAt ( i ) === "\t" ) { hasIndent += formatOptions . tabSize ! ; } else { break ; } } if ( preferredIndent !== hasIndent ) { const firstNoWhiteSpacePosition = absolutePosition + i ; edits . push ( { span : createTextSpanFromBounds ( absolutePosition , firstNoWhiteSpacePosition ) , newText : formatting . getIndentationString ( preferredIndent , formatOptions ) } ) ; } } } if ( ! edits ) { return undefined ; } return edits . map ( ( edit ) => { return { start : scriptInfo . positionToLineOffset ( edit . span . start ) , end : scriptInfo . positionToLineOffset ( textSpanEnd ( edit . span ) ) , newText : edit . newText ? edit . newText : "" } ; } ) ; } private getCompletions ( args , kind ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const position = this . getPosition ( args , scriptInfo ) ; const completions = project . getLanguageService ( ) . getCompletionsAtPosition ( file , position , { ... convertUserPreferences ( this . getPreferences ( file ) ) , triggerCharacter : args . triggerCharacter , triggerKind : args . triggerKind as CompletionTriggerKind | undefined , includeExternalModuleExports : args . includeExternalModuleExports , includeInsertTextCompletions : args . includeInsertTextCompletions , } , project . projectService . getFormatCodeOptions ( file ) ) ; if ( completions === undefined ) return undefined ; if ( kind === protocol . CommandTypes . CompletionsFull ) return completions ; const prefix = args . prefix || "" ; const entries = mapDefined < CompletionEntry , protocol . CompletionEntry > ( completions . entries , entry => { if ( completions . isMemberCompletion || startsWith ( entry . name . toLowerCase ( ) , prefix . toLowerCase ( ) ) ) { const { name , kind , kindModifiers , sortText , insertText , replacementSpan , hasAction , source , sourceDisplay , isSnippet , isRecommended , isPackageJsonImport , isImportStatementCompletion , data } = entry ; const convertedSpan = replacementSpan ? toProtocolTextSpan ( replacementSpan , scriptInfo ) : undefined ; return { name , kind , kindModifiers , sortText , insertText , replacementSpan : convertedSpan , isSnippet , hasAction : hasAction || undefined , source , sourceDisplay , isRecommended , isPackageJsonImport , isImportStatementCompletion , data } ; } } ) ; if ( kind === protocol . CommandTypes . Completions ) { if ( completions . metadata ) ( entries as WithMetadata < readonly protocol . CompletionEntry [ ] > ) . metadata = completions . metadata ; return entries ; } const res = { ... completions , optionalReplacementSpan : completions . optionalReplacementSpan && toProtocolTextSpan ( completions . optionalReplacementSpan , scriptInfo ) , entries , } ; return res ; } private getCompletionEntryDetails ( args , fullResult ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const position = this . getPosition ( args , scriptInfo ) ; const formattingOptions = project . projectService . getFormatCodeOptions ( file ) ; const useDisplayParts = ! ! this . getPreferences ( file ) . displayPartsForJSDoc ; const result = mapDefined ( args . entryNames , entryName => { const { name , source , data } = typeof entryName === "string" ? { name : entryName , source : undefined , data : undefined } : entryName ; return project . getLanguageService ( ) . getCompletionEntryDetails ( file , position , name , formattingOptions , source , this . getPreferences ( file ) , data ? cast ( data , isCompletionEntryData ) : undefined ) ; } ) ; return fullResult ? ( useDisplayParts ? result : result . map ( details => ( { ... details , tags : this . mapJSDocTagInfo ( details . tags , project , false ) as JSDocTagInfo [ ] } ) ) ) : result . map ( details => ( { ... details , codeActions : map ( details . codeActions , action => this . mapCodeAction ( action ) ) , documentation : this . mapDisplayParts ( details . documentation , project ) , tags : this . mapJSDocTagInfo ( details . tags , project , useDisplayParts ) , } ) ) ; } private getCompileOnSaveAffectedFileList ( args ) { const projects = this . getProjects ( args , true , true ) ; const info = this . projectService . getScriptInfo ( args . file ) ; if ( ! info ) { return emptyArray ; } return combineProjectOutput ( info , path => this . projectService . getScriptInfoForPath ( path ) ! , projects , ( project , info ) => { if ( ! project . compileOnSaveEnabled || ! project . languageServiceEnabled || project . isOrphan ( ) ) { return undefined ; } const compilationSettings = project . getCompilationSettings ( ) ; if ( ! ! compilationSettings . noEmit || fileExtensionIs ( info . fileName , Extension . Dts ) && ! dtsChangeCanAffectEmit ( compilationSettings ) ) { return undefined ; } return { projectFileName : project . getProjectName ( ) , fileNames : project . getCompileOnSaveAffectedFileList ( info ) , projectUsesOutFile : ! ! outFile ( compilationSettings ) } ; } ) ; } private emitFile ( args ) { const { file , project } = this . getFileAndProject ( args ) ; if ( ! project ) { Errors . ThrowNoProject ( ) ; } if ( ! project . languageServiceEnabled ) { return args . richResponse ? { emitSkipped : true , diagnostics : [ ] } : false ; } const scriptInfo = project . getScriptInfo ( file ) ! ; const { emitSkipped , diagnostics } = project . emitFile ( scriptInfo , ( path , data , writeByteOrderMark ) => this . host . writeFile ( path , data , writeByteOrderMark ) ) ; return args . richResponse ? { emitSkipped , diagnostics : args . includeLinePosition ? this . convertToDiagnosticsWithLinePositionFromDiagnosticFile ( diagnostics ) : diagnostics . map ( d => formatDiagnosticToProtocol ( d , true ) ) } : ! emitSkipped ; } private getSignatureHelpItems ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const position = this . getPosition ( args , scriptInfo ) ; const helpItems = project . getLanguageService ( ) . getSignatureHelpItems ( file , position , args ) ; const useDisplayParts = ! ! this . getPreferences ( file ) . displayPartsForJSDoc ; if ( helpItems && simplifiedResult ) { const span = helpItems . applicableSpan ; return { ... helpItems , applicableSpan : { start : scriptInfo . positionToLineOffset ( span . start ) , end : scriptInfo . positionToLineOffset ( span . start + span . length ) } , items : this . mapSignatureHelpItems ( helpItems . items , project , useDisplayParts ) , } ; } else if ( useDisplayParts || ! helpItems ) { return helpItems ; } else { return { ... helpItems , items : helpItems . items . map ( item => ( { ... item , tags : this . mapJSDocTagInfo ( item . tags , project , false ) as JSDocTagInfo [ ] } ) ) } ; } } private toPendingErrorCheck ( uncheckedFileName ) { const fileName = toNormalizedPath ( uncheckedFileName ) ; const project = this . projectService . tryGetDefaultProjectForFile ( fileName ) ; return project && { fileName , project } ; } private getDiagnostics ( next , delay , fileNames ) { if ( this . suppressDiagnosticEvents ) { return ; } if ( fileNames . length > 0 ) { this . updateErrorCheck ( next , fileNames , delay ) ; } } private change ( args ) { const scriptInfo = this . projectService . getScriptInfo ( args . file ) ! ; Debug . assert ( ! ! scriptInfo ) ; const start = scriptInfo . lineOffsetToPosition ( args . line , args . offset ) ; const end = scriptInfo . lineOffsetToPosition ( args . endLine , args . endOffset ) ; if ( start >= 0 ) { this . changeSeq ++ ; this . projectService . applyChangesToFile ( scriptInfo , singleIterator ( { span : { start , length : end - start } , newText : args . insertString ! } ) ) ; } } private reload ( args , reqSeq ) { const file = toNormalizedPath ( args . file ) ; const tempFileName = args . tmpfile === undefined ? undefined : toNormalizedPath ( args . tmpfile ) ; const info = this . projectService . getScriptInfoForNormalizedPath ( file ) ; if ( info ) { this . changeSeq ++ ; if ( info . reloadFromFile ( tempFileName ) ) { this . doOutput ( undefined , CommandNames . Reload , reqSeq , true ) ; } } } private saveToTmp ( fileName , tempFileName ) { const scriptInfo = this . projectService . getScriptInfo ( fileName ) ; if ( scriptInfo ) { scriptInfo . saveTo ( tempFileName ) ; } } private closeClientFile ( fileName ) { if ( ! fileName ) { return ; } const file = normalizePath ( fileName ) ; this . projectService . closeClientFile ( file ) ; } private mapLocationNavigationBarItems ( items , scriptInfo ) { return map ( items , item => ( { text : item . text , kind : item . kind , kindModifiers : item . kindModifiers , spans : item . spans . map ( span => toProtocolTextSpan ( span , scriptInfo ) ) , childItems : this . mapLocationNavigationBarItems ( item . childItems , scriptInfo ) , indent : item . indent } ) ) ; } private getNavigationBarItems ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const items = languageService . getNavigationBarItems ( file ) ; return ! items ? undefined : simplifiedResult ? this . mapLocationNavigationBarItems ( items , this . projectService . getScriptInfoForNormalizedPath ( file ) ! ) : items ; } private toLocationNavigationTree ( tree , scriptInfo ) { return { text : tree . text , kind : tree . kind , kindModifiers : tree . kindModifiers , spans : tree . spans . map ( span => toProtocolTextSpan ( span , scriptInfo ) ) , nameSpan : tree . nameSpan && toProtocolTextSpan ( tree . nameSpan , scriptInfo ) , childItems : map ( tree . childItems , item => this . toLocationNavigationTree ( item , scriptInfo ) ) } ; } private getNavigationTree ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const tree = languageService . getNavigationTree ( file ) ; return ! tree ? undefined : simplifiedResult ? this . toLocationNavigationTree ( tree , this . projectService . getScriptInfoForNormalizedPath ( file ) ! ) : tree ; } private getNavigateToItems ( args , simplifiedResult ) { const full = this . getFullNavigateToItems ( args ) ; return ! simplifiedResult ? flattenCombineOutputResult ( full ) : flatMap ( full , ( { project , result } ) => result . map ( navItem => { const scriptInfo = project . getScriptInfo ( navItem . fileName ) ! ; const bakedItem = { name : navItem . name , kind : navItem . kind , kindModifiers : navItem . kindModifiers , isCaseSensitive : navItem . isCaseSensitive , matchKind : navItem . matchKind , file : navItem . fileName , start : scriptInfo . positionToLineOffset ( navItem . textSpan . start ) , end : scriptInfo . positionToLineOffset ( textSpanEnd ( navItem . textSpan ) ) } ; if ( navItem . kindModifiers && ( navItem . kindModifiers !== "" ) ) { bakedItem . kindModifiers = navItem . kindModifiers ; } if ( navItem . containerName && ( navItem . containerName . length > 0 ) ) { bakedItem . containerName = navItem . containerName ; } if ( navItem . containerKind && ( navItem . containerKind . length > 0 ) ) { bakedItem . containerKind = navItem . containerKind ; } return bakedItem ; } ) ) ; } private getFullNavigateToItems ( args ) { const { currentFileOnly , searchValue , maxResultCount , projectFileName } = args ; if ( currentFileOnly ) { Debug . assertIsDefined ( args . file ) ; const { file , project } = this . getFileAndProject ( args as protocol . FileRequestArgs ) ; return [ { project , result : project . getLanguageService ( ) . getNavigateToItems ( searchValue , maxResultCount , file ) } ] ; } else if ( ! args . file && ! projectFileName ) { return combineProjectOutputFromEveryProject ( this . projectService , project => project . getLanguageService ( ) . getNavigateToItems ( searchValue , maxResultCount , undefined , project . isNonTsProject ( ) ) , navigateToItemIsEqualTo ) ; } const fileArgs = args as protocol . FileRequestArgs ; return combineProjectOutputWhileOpeningReferencedProjects < NavigateToItem > ( this . getProjects ( fileArgs ) , this . getDefaultProject ( fileArgs ) , project => project . getLanguageService ( ) . getNavigateToItems ( searchValue , maxResultCount , undefined , project . isNonTsProject ( ) ) , documentSpanLocation , navigateToItemIsEqualTo ) ; function navigateToItemIsEqualTo ( a , b ) { if ( a === b ) { return true ; } if ( ! a || ! b ) { return false ; } return a . containerKind === b . containerKind && a . containerName === b . containerName && a . fileName === b . fileName && a . isCaseSensitive === b . isCaseSensitive && a . kind === b . kind && a . kindModifiers === b . kindModifiers && a . matchKind === b . matchKind && a . name === b . name && a . textSpan . start === b . textSpan . start && a . textSpan . length === b . textSpan . length ; } } private getSupportedCodeFixes ( ) { return getSupportedCodeFixes ( ) ; } private isLocation ( locationOrSpan ) { return ( locationOrSpan as protocol . FileLocationRequestArgs ) . line !== undefined ; } private extractPositionOrRange ( args , scriptInfo ) { let position ; let textRange ; if ( this . isLocation ( args ) ) { position = getPosition ( args ) ; } else { textRange = this . getRange ( args , scriptInfo ) ; } return Debug . checkDefined ( position === undefined ? textRange : position ) ; function getPosition ( loc ) { return loc . position !== undefined ? loc . position : scriptInfo . lineOffsetToPosition ( loc . line , loc . offset ) ; } } private getRange ( args , scriptInfo ) { const { startPosition , endPosition } = this . getStartAndEndPosition ( args , scriptInfo ) ; return { pos : startPosition , end : endPosition } ; } private getApplicableRefactors ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ! ; return project . getLanguageService ( ) . getApplicableRefactors ( file , this . extractPositionOrRange ( args , scriptInfo ) , this . getPreferences ( file ) , args . triggerReason , args . kind ) ; } private getEditsForRefactor ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ! ; const result = project . getLanguageService ( ) . getEditsForRefactor ( file , this . getFormatOptions ( file ) , this . extractPositionOrRange ( args , scriptInfo ) , args . refactor , args . action , this . getPreferences ( file ) ) ; if ( result === undefined ) { return { edits : [ ] } ; } if ( simplifiedResult ) { const { renameFilename , renameLocation , edits } = result ; let mappedRenameLocation ; if ( renameFilename !== undefined && renameLocation !== undefined ) { const renameScriptInfo = project . getScriptInfoForNormalizedPath ( toNormalizedPath ( renameFilename ) ) ! ; mappedRenameLocation = getLocationInNewDocument ( getSnapshotText ( renameScriptInfo . getSnapshot ( ) ) , renameFilename , renameLocation , edits ) ; } return { renameLocation : mappedRenameLocation , renameFilename , edits : this . mapTextChangesToCodeEdits ( edits ) } ; } else { return result ; } } private organizeImports ( args , simplifiedResult ) { Debug . assert ( args . scope . type === "file" ) ; const { file , project } = this . getFileAndProject ( args . scope . args ) ; const changes = project . getLanguageService ( ) . organizeImports ( { fileName : file , skipDestructiveCodeActions : args . skipDestructiveCodeActions , type : "file" , } , this . getFormatOptions ( file ) , this . getPreferences ( file ) ) ; if ( simplifiedResult ) { return this . mapTextChangesToCodeEdits ( changes ) ; } else { return changes ; } } private getEditsForFileRename ( args , simplifiedResult ) { const oldPath = toNormalizedPath ( args . oldFilePath ) ; const newPath = toNormalizedPath ( args . newFilePath ) ; const formatOptions = this . getHostFormatOptions ( ) ; const preferences = this . getHostPreferences ( ) ; const changes = flattenCombineOutputResult ( combineProjectOutputFromEveryProject ( this . projectService , project => project . getLanguageService ( ) . getEditsForFileRename ( oldPath , newPath , formatOptions , preferences ) , ( a , b ) => a . fileName === b . fileName ) ) ; return simplifiedResult ? changes . map ( c => this . mapTextChangeToCodeEdit ( c ) ) : changes ; } private getCodeFixes ( args , simplifiedResult ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = project . getScriptInfoForNormalizedPath ( file ) ! ; const { startPosition , endPosition } = this . getStartAndEndPosition ( args , scriptInfo ) ; let codeActions ; try { codeActions = project . getLanguageService ( ) . getCodeFixesAtPosition ( file , startPosition , endPosition , args . errorCodes , this . getFormatOptions ( file ) , this . getPreferences ( file ) ) ; } catch ( e ) { const ls = project . getLanguageService ( ) ; const existingDiagCodes = [ ... ls . getSyntacticDiagnostics ( file ) , ... ls . getSemanticDiagnostics ( file ) , ... ls . getSuggestionDiagnostics ( file ) ] . map ( d => decodedTextSpanIntersectsWith ( startPosition , endPosition - startPosition , d . start ! , d . length ! ) && d . code ) ; const badCode = args . errorCodes . find ( c => ! existingDiagCodes . includes ( c ) ) ; if ( badCode !== undefined ) { e . message = `BADCLIENT: Bad error code, ${ badCode } not found in range ${ startPosition }..${ endPosition } (found: ${ existingDiagCodes . join ( ", " ) }); could have caused this error:\n${ e . message }` ; } throw e ; } return simplifiedResult ? codeActions . map ( codeAction => this . mapCodeFixAction ( codeAction ) ) : codeActions ; } private getCombinedCodeFix ( { scope , fixId } , simplifiedResult ) { Debug . assert ( scope . type === "file" ) ; const { file , project } = this . getFileAndProject ( scope . args ) ; const res = project . getLanguageService ( ) . getCombinedCodeFix ( { type : "file" , fileName : file } , fixId , this . getFormatOptions ( file ) , this . getPreferences ( file ) ) ; if ( simplifiedResult ) { return { changes : this . mapTextChangesToCodeEdits ( res . changes ) , commands : res . commands } ; } else { return res ; } } private applyCodeActionCommand ( args ) { const commands = args . command as CodeActionCommand | CodeActionCommand [ ] ; for ( const command of toArray ( commands ) ) { const { file , project } = this . getFileAndProject ( command ) ; project . getLanguageService ( ) . applyCodeActionCommand ( command , this . getFormatOptions ( file ) ) . then ( _result => { } , _error => { } ) ; } return { } ; } private getStartAndEndPosition ( args , scriptInfo ) { let startPosition , endPosition ; if ( args . startPosition !== undefined ) { startPosition = args . startPosition ; } else { startPosition = scriptInfo . lineOffsetToPosition ( args . startLine , args . startOffset ) ; args . startPosition = startPosition ; } if ( args . endPosition !== undefined ) { endPosition = args . endPosition ; } else { endPosition = scriptInfo . lineOffsetToPosition ( args . endLine , args . endOffset ) ; args . endPosition = endPosition ; } return { startPosition , endPosition } ; } private mapCodeAction ( { description , changes , commands } ) { return { description , changes : this . mapTextChangesToCodeEdits ( changes ) , commands } ; } private mapCodeFixAction ( { fixName , description , changes , commands , fixId , fixAllDescription } ) { return { fixName , description , changes : this . mapTextChangesToCodeEdits ( changes ) , commands , fixId , fixAllDescription } ; } private mapTextChangesToCodeEdits ( textChanges ) { return textChanges . map ( change => this . mapTextChangeToCodeEdit ( change ) ) ; } private mapTextChangeToCodeEdit ( textChanges ) { const scriptInfo = this . projectService . getScriptInfoOrConfig ( textChanges . fileName ) ; if ( ! ! textChanges . isNewFile === ! ! scriptInfo ) { if ( ! scriptInfo ) { this . projectService . logErrorForScriptInfoNotFound ( textChanges . fileName ) ; } Debug . fail ( "Expected isNewFile for (only) new files. " + JSON . stringify ( { isNewFile : ! ! textChanges . isNewFile , hasScriptInfo : ! ! scriptInfo } ) ) ; } return scriptInfo ? { fileName : textChanges . fileName , textChanges : textChanges . textChanges . map ( textChange => convertTextChangeToCodeEdit ( textChange , scriptInfo ) ) } : convertNewFileTextChangeToCodeEdit ( textChanges ) ; } private convertTextChangeToCodeEdit ( change , scriptInfo ) { return { start : scriptInfo . positionToLineOffset ( change . span . start ) , end : scriptInfo . positionToLineOffset ( change . span . start + change . span . length ) , newText : change . newText ? change . newText : "" } ; } private getBraceMatching ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const position = this . getPosition ( args , scriptInfo ) ; const spans = languageService . getBraceMatchingAtPosition ( file , position ) ; return ! spans ? undefined : simplifiedResult ? spans . map ( span => toProtocolTextSpan ( span , scriptInfo ) ) : spans ; } private getDiagnosticsForProject ( next , delay , fileName ) { if ( this . suppressDiagnosticEvents ) { return ; } const { fileNames , languageServiceDisabled } = this . getProjectInfoWorker ( fileName , undefined , true , true ) ; if ( languageServiceDisabled ) { return ; } const fileNamesInProject = fileNames ! . filter ( value => ! stringContains ( value , "lib.d.ts" ) ) ; if ( fileNamesInProject . length === 0 ) { return ; } const highPriorityFiles = [ ] ; const mediumPriorityFiles = [ ] ; const lowPriorityFiles = [ ] ; const veryLowPriorityFiles = [ ] ; const normalizedFileName = toNormalizedPath ( fileName ) ; const project = this . projectService . ensureDefaultProjectForFile ( normalizedFileName ) ; for ( const fileNameInProject of fileNamesInProject ) { if ( this . getCanonicalFileName ( fileNameInProject ) === this . getCanonicalFileName ( fileName ) ) { highPriorityFiles . push ( fileNameInProject ) ; } else { const info = this . projectService . getScriptInfo ( fileNameInProject ) ! ; if ( ! info . isScriptOpen ( ) ) { if ( fileExtensionIs ( fileNameInProject , Extension . Dts ) ) { veryLowPriorityFiles . push ( fileNameInProject ) ; } else { lowPriorityFiles . push ( fileNameInProject ) ; } } else { mediumPriorityFiles . push ( fileNameInProject ) ; } } } const sortedFiles = [ ... highPriorityFiles , ... mediumPriorityFiles , ... lowPriorityFiles , ... veryLowPriorityFiles ] ; const checkList = sortedFiles . map ( fileName => ( { fileName , project } ) ) ; this . updateErrorCheck ( next , checkList , delay , false ) ; } private configurePlugin ( args ) { this . projectService . configurePlugin ( args ) ; } private getSmartSelectionRange ( args , simplifiedResult ) { const { locations } = args ; const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = Debug . checkDefined ( this . projectService . getScriptInfo ( file ) ) ; return map ( locations , location => { const pos = this . getPosition ( location , scriptInfo ) ; const selectionRange = languageService . getSmartSelectionRange ( file , pos ) ; return simplifiedResult ? this . mapSelectionRange ( selectionRange , scriptInfo ) : selectionRange ; } ) ; } private toggleLineComment ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfo ( file ) ! ; const textRange = this . getRange ( args , scriptInfo ) ; const textChanges = languageService . toggleLineComment ( file , textRange ) ; if ( simplifiedResult ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return textChanges . map ( textChange => this . convertTextChangeToCodeEdit ( textChange , scriptInfo ) ) ; } return textChanges ; } private toggleMultilineComment ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const textRange = this . getRange ( args , scriptInfo ) ; const textChanges = languageService . toggleMultilineComment ( file , textRange ) ; if ( simplifiedResult ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return textChanges . map ( textChange => this . convertTextChangeToCodeEdit ( textChange , scriptInfo ) ) ; } return textChanges ; } private commentSelection ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const textRange = this . getRange ( args , scriptInfo ) ; const textChanges = languageService . commentSelection ( file , textRange ) ; if ( simplifiedResult ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return textChanges . map ( textChange => this . convertTextChangeToCodeEdit ( textChange , scriptInfo ) ) ; } return textChanges ; } private uncommentSelection ( args , simplifiedResult ) { const { file , languageService } = this . getFileAndLanguageServiceForSyntacticOperation ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; const textRange = this . getRange ( args , scriptInfo ) ; const textChanges = languageService . uncommentSelection ( file , textRange ) ; if ( simplifiedResult ) { const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ! ; return textChanges . map ( textChange => this . convertTextChangeToCodeEdit ( textChange , scriptInfo ) ) ; } return textChanges ; } private mapSelectionRange ( selectionRange , scriptInfo ) { const result = { textSpan : toProtocolTextSpan ( selectionRange . textSpan , scriptInfo ) , } ; if ( selectionRange . parent ) { result . parent = this . mapSelectionRange ( selectionRange . parent , scriptInfo ) ; } return result ; } private getScriptInfoFromProjectService ( file ) { const normalizedFile = toNormalizedPath ( file ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( normalizedFile ) ; if ( ! scriptInfo ) { this . projectService . logErrorForScriptInfoNotFound ( normalizedFile ) ; return Errors . ThrowNoProject ( ) ; } return scriptInfo ; } private toProtocolCallHierarchyItem ( item ) { const scriptInfo = this . getScriptInfoFromProjectService ( item . file ) ; return { name : item . name , kind : item . kind , kindModifiers : item . kindModifiers , file : item . file , containerName : item . containerName , span : toProtocolTextSpan ( item . span , scriptInfo ) , selectionSpan : toProtocolTextSpan ( item . selectionSpan , scriptInfo ) } ; } private toProtocolCallHierarchyIncomingCall ( incomingCall ) { const scriptInfo = this . getScriptInfoFromProjectService ( incomingCall . from . file ) ; return { from : this . toProtocolCallHierarchyItem ( incomingCall . from ) , fromSpans : incomingCall . fromSpans . map ( fromSpan => toProtocolTextSpan ( fromSpan , scriptInfo ) ) } ; } private toProtocolCallHierarchyOutgoingCall ( outgoingCall , scriptInfo ) { return { to : this . toProtocolCallHierarchyItem ( outgoingCall . to ) , fromSpans : outgoingCall . fromSpans . map ( fromSpan => toProtocolTextSpan ( fromSpan , scriptInfo ) ) } ; } private prepareCallHierarchy ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . projectService . getScriptInfoForNormalizedPath ( file ) ; if ( scriptInfo ) { const position = this . getPosition ( args , scriptInfo ) ; const result = project . getLanguageService ( ) . prepareCallHierarchy ( file , position ) ; return result && mapOneOrMany ( result , item => this . toProtocolCallHierarchyItem ( item ) ) ; } return undefined ; } private provideCallHierarchyIncomingCalls ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . getScriptInfoFromProjectService ( file ) ; const incomingCalls = project . getLanguageService ( ) . provideCallHierarchyIncomingCalls ( file , this . getPosition ( args , scriptInfo ) ) ; return incomingCalls . map ( call => this . toProtocolCallHierarchyIncomingCall ( call ) ) ; } private provideCallHierarchyOutgoingCalls ( args ) { const { file , project } = this . getFileAndProject ( args ) ; const scriptInfo = this . getScriptInfoFromProjectService ( file ) ; const outgoingCalls = project . getLanguageService ( ) . provideCallHierarchyOutgoingCalls ( file , this . getPosition ( args , scriptInfo ) ) ; return outgoingCalls . map ( call => this . toProtocolCallHierarchyOutgoingCall ( call , scriptInfo ) ) ; } getCanonicalFileName ( fileName ) { const name = this . host . useCaseSensitiveFileNames ? fileName : toFileNameLowerCase ( fileName ) ; return normalizePath ( name ) ; } exit ( ) { } private notRequired ( ) { return { responseRequired : false } ; } private requiredResponse ( response ) { return { response , responseRequired : true } ; } private handlers = new Map ( getEntries < ( request : protocol . Request ) => HandlerResponse > ( { [ CommandNames . Status ] : ( ) => { const response = { version : ts . version } ; return this . requiredResponse ( response ) ; } , [ CommandNames . OpenExternalProject ] : ( request ) => { this . projectService . openExternalProject ( request . arguments ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . OpenExternalProjects ] : ( request ) => { this . projectService . openExternalProjects ( request . arguments . projects ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . CloseExternalProject ] : ( request ) => { this . projectService . closeExternalProject ( request . arguments . projectFileName ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . SynchronizeProjectList ] : ( request ) => { const result = this . projectService . synchronizeProjectList ( request . arguments . knownProjects , request . arguments . includeProjectReferenceRedirectInfo ) ; if ( ! result . some ( p => p . projectErrors && p . projectErrors . length !== 0 ) ) { return this . requiredResponse ( result ) ; } const converted = map ( result , p => { if ( ! p . projectErrors || p . projectErrors . length === 0 ) { return p ; } return { info : p . info , changes : p . changes , files : p . files , projectErrors : this . convertToDiagnosticsWithLinePosition ( p . projectErrors , undefined ) } ; } ) ; return this . requiredResponse ( converted ) ; } , [ CommandNames . UpdateOpen ] : ( request ) => { this . changeSeq ++ ; this . projectService . applyChangesInOpenFiles ( request . arguments . openFiles && mapIterator ( arrayIterator ( request . arguments . openFiles ) , file => ( { fileName : file . file , content : file . fileContent , scriptKind : file . scriptKindName , projectRootPath : file . projectRootPath } ) ) , request . arguments . changedFiles && mapIterator ( arrayIterator ( request . arguments . changedFiles ) , file => ( { fileName : file . fileName , changes : mapDefinedIterator ( arrayReverseIterator ( file . textChanges ) , change => { const scriptInfo = Debug . checkDefined ( this . projectService . getScriptInfo ( file . fileName ) ) ; const start = scriptInfo . lineOffsetToPosition ( change . start . line , change . start . offset ) ; const end = scriptInfo . lineOffsetToPosition ( change . end . line , change . end . offset ) ; return start >= 0 ? { span : { start , length : end - start } , newText : change . newText } : undefined ; } ) } ) ) , request . arguments . closedFiles ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . ApplyChangedToOpenFiles ] : ( request ) => { this . changeSeq ++ ; this . projectService . applyChangesInOpenFiles ( request . arguments . openFiles && arrayIterator ( request . arguments . openFiles ) , request . arguments . changedFiles && mapIterator ( arrayIterator ( request . arguments . changedFiles ) , file => ( { fileName : file . fileName , changes : arrayReverseIterator ( file . changes ) } ) ) , request . arguments . closedFiles ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . Exit ] : ( ) => { this . exit ( ) ; return this . notRequired ( ) ; } , [ CommandNames . Definition ] : ( request ) => { return this . requiredResponse ( this . getDefinition ( request . arguments , true ) ) ; } , [ CommandNames . DefinitionFull ] : ( request ) => { return this . requiredResponse ( this . getDefinition ( request . arguments , false ) ) ; } , [ CommandNames . DefinitionAndBoundSpan ] : ( request ) => { return this . requiredResponse ( this . getDefinitionAndBoundSpan ( request . arguments , true ) ) ; } , [ CommandNames . DefinitionAndBoundSpanFull ] : ( request ) => { return this . requiredResponse ( this . getDefinitionAndBoundSpan ( request . arguments , false ) ) ; } , [ CommandNames . EmitOutput ] : ( request ) => { return this . requiredResponse ( this . getEmitOutput ( request . arguments ) ) ; } , [ CommandNames . TypeDefinition ] : ( request ) => { return this . requiredResponse ( this . getTypeDefinition ( request . arguments ) ) ; } , [ CommandNames . Implementation ] : ( request ) => { return this . requiredResponse ( this . getImplementation ( request . arguments , true ) ) ; } , [ CommandNames . ImplementationFull ] : ( request ) => { return this . requiredResponse ( this . getImplementation ( request . arguments , false ) ) ; } , [ CommandNames . References ] : ( request ) => { return this . requiredResponse ( this . getReferences ( request . arguments , true ) ) ; } , [ CommandNames . ReferencesFull ] : ( request ) => { return this . requiredResponse ( this . getReferences ( request . arguments , false ) ) ; } , [ CommandNames . Rename ] : ( request ) => { return this . requiredResponse ( this . getRenameLocations ( request . arguments , true ) ) ; } , [ CommandNames . RenameLocationsFull ] : ( request ) => { return this . requiredResponse ( this . getRenameLocations ( request . arguments , false ) ) ; } , [ CommandNames . RenameInfoFull ] : ( request ) => { return this . requiredResponse ( this . getRenameInfo ( request . arguments ) ) ; } , [ CommandNames . Open ] : ( request ) => { this . openClientFile ( toNormalizedPath ( request . arguments . file ) , request . arguments . fileContent , convertScriptKindName ( request . arguments . scriptKindName ! ) , request . arguments . projectRootPath ? toNormalizedPath ( request . arguments . projectRootPath ) : undefined ) ; return this . notRequired ( ) ; } , [ CommandNames . Quickinfo ] : ( request ) => { return this . requiredResponse ( this . getQuickInfoWorker ( request . arguments , true ) ) ; } , [ CommandNames . QuickinfoFull ] : ( request ) => { return this . requiredResponse ( this . getQuickInfoWorker ( request . arguments , false ) ) ; } , [ CommandNames . GetOutliningSpans ] : ( request ) => { return this . requiredResponse ( this . getOutliningSpans ( request . arguments , true ) ) ; } , [ CommandNames . GetOutliningSpansFull ] : ( request ) => { return this . requiredResponse ( this . getOutliningSpans ( request . arguments , false ) ) ; } , [ CommandNames . TodoComments ] : ( request ) => { return this . requiredResponse ( this . getTodoComments ( request . arguments ) ) ; } , [ CommandNames . Indentation ] : ( request ) => { return this . requiredResponse ( this . getIndentation ( request . arguments ) ) ; } , [ CommandNames . NameOrDottedNameSpan ] : ( request ) => { return this . requiredResponse ( this . getNameOrDottedNameSpan ( request . arguments ) ) ; } , [ CommandNames . BreakpointStatement ] : ( request ) => { return this . requiredResponse ( this . getBreakpointStatement ( request . arguments ) ) ; } , [ CommandNames . BraceCompletion ] : ( request ) => { return this . requiredResponse ( this . isValidBraceCompletion ( request . arguments ) ) ; } , [ CommandNames . DocCommentTemplate ] : ( request ) => { return this . requiredResponse ( this . getDocCommentTemplate ( request . arguments ) ) ; } , [ CommandNames . GetSpanOfEnclosingComment ] : ( request ) => { return this . requiredResponse ( this . getSpanOfEnclosingComment ( request . arguments ) ) ; } , [ CommandNames . FileReferences ] : ( request ) => { return this . requiredResponse ( this . getFileReferences ( request . arguments , true ) ) ; } , [ CommandNames . FileReferencesFull ] : ( request ) => { return this . requiredResponse ( this . getFileReferences ( request . arguments , false ) ) ; } , [ CommandNames . Format ] : ( request ) => { return this . requiredResponse ( this . getFormattingEditsForRange ( request . arguments ) ) ; } , [ CommandNames . Formatonkey ] : ( request ) => { return this . requiredResponse ( this . getFormattingEditsAfterKeystroke ( request . arguments ) ) ; } , [ CommandNames . FormatFull ] : ( request ) => { return this . requiredResponse ( this . getFormattingEditsForDocumentFull ( request . arguments ) ) ; } , [ CommandNames . FormatonkeyFull ] : ( request ) => { return this . requiredResponse ( this . getFormattingEditsAfterKeystrokeFull ( request . arguments ) ) ; } , [ CommandNames . FormatRangeFull ] : ( request ) => { return this . requiredResponse ( this . getFormattingEditsForRangeFull ( request . arguments ) ) ; } , [ CommandNames . CompletionInfo ] : ( request ) => { return this . requiredResponse ( this . getCompletions ( request . arguments , CommandNames . CompletionInfo ) ) ; } , [ CommandNames . Completions ] : ( request ) => { return this . requiredResponse ( this . getCompletions ( request . arguments , CommandNames . Completions ) ) ; } , [ CommandNames . CompletionsFull ] : ( request ) => { return this . requiredResponse ( this . getCompletions ( request . arguments , CommandNames . CompletionsFull ) ) ; } , [ CommandNames . CompletionDetails ] : ( request ) => { return this . requiredResponse ( this . getCompletionEntryDetails ( request . arguments , false ) ) ; } , [ CommandNames . CompletionDetailsFull ] : ( request ) => { return this . requiredResponse ( this . getCompletionEntryDetails ( request . arguments , true ) ) ; } , [ CommandNames . CompileOnSaveAffectedFileList ] : ( request ) => { return this . requiredResponse ( this . getCompileOnSaveAffectedFileList ( request . arguments ) ) ; } , [ CommandNames . CompileOnSaveEmitFile ] : ( request ) => { return this . requiredResponse ( this . emitFile ( request . arguments ) ) ; } , [ CommandNames . SignatureHelp ] : ( request ) => { return this . requiredResponse ( this . getSignatureHelpItems ( request . arguments , true ) ) ; } , [ CommandNames . SignatureHelpFull ] : ( request ) => { return this . requiredResponse ( this . getSignatureHelpItems ( request . arguments , false ) ) ; } , [ CommandNames . CompilerOptionsDiagnosticsFull ] : ( request ) => { return this . requiredResponse ( this . getCompilerOptionsDiagnostics ( request . arguments ) ) ; } , [ CommandNames . EncodedSyntacticClassificationsFull ] : ( request ) => { return this . requiredResponse ( this . getEncodedSyntacticClassifications ( request . arguments ) ) ; } , [ CommandNames . EncodedSemanticClassificationsFull ] : ( request ) => { return this . requiredResponse ( this . getEncodedSemanticClassifications ( request . arguments ) ) ; } , [ CommandNames . Cleanup ] : ( ) => { this . cleanup ( ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . SemanticDiagnosticsSync ] : ( request ) => { return this . requiredResponse ( this . getSemanticDiagnosticsSync ( request . arguments ) ) ; } , [ CommandNames . SyntacticDiagnosticsSync ] : ( request ) => { return this . requiredResponse ( this . getSyntacticDiagnosticsSync ( request . arguments ) ) ; } , [ CommandNames . SuggestionDiagnosticsSync ] : ( request ) => { return this . requiredResponse ( this . getSuggestionDiagnosticsSync ( request . arguments ) ) ; } , [ CommandNames . Geterr ] : ( request ) => { this . errorCheck . startNew ( next => this . getDiagnostics ( next , request . arguments . delay , request . arguments . files ) ) ; return this . notRequired ( ) ; } , [ CommandNames . GeterrForProject ] : ( request ) => { this . errorCheck . startNew ( next => this . getDiagnosticsForProject ( next , request . arguments . delay , request . arguments . file ) ) ; return this . notRequired ( ) ; } , [ CommandNames . Change ] : ( request ) => { this . change ( request . arguments ) ; return this . notRequired ( ) ; } , [ CommandNames . Configure ] : ( request ) => { this . projectService . setHostConfiguration ( request . arguments ) ; this . doOutput ( undefined , CommandNames . Configure , request . seq , true ) ; return this . notRequired ( ) ; } , [ CommandNames . Reload ] : ( request ) => { this . reload ( request . arguments , request . seq ) ; return this . requiredResponse ( { reloadFinished : true } ) ; } , [ CommandNames . Saveto ] : ( request ) => { const savetoArgs = request . arguments as protocol . SavetoRequestArgs ; this . saveToTmp ( savetoArgs . file , savetoArgs . tmpfile ) ; return this . notRequired ( ) ; } , [ CommandNames . Close ] : ( request ) => { const closeArgs = request . arguments as protocol . FileRequestArgs ; this . closeClientFile ( closeArgs . file ) ; return this . notRequired ( ) ; } , [ CommandNames . Navto ] : ( request ) => { return this . requiredResponse ( this . getNavigateToItems ( request . arguments , true ) ) ; } , [ CommandNames . NavtoFull ] : ( request ) => { return this . requiredResponse ( this . getNavigateToItems ( request . arguments , false ) ) ; } , [ CommandNames . Brace ] : ( request ) => { return this . requiredResponse ( this . getBraceMatching ( request . arguments , true ) ) ; } , [ CommandNames . BraceFull ] : ( request ) => { return this . requiredResponse ( this . getBraceMatching ( request . arguments , false ) ) ; } , [ CommandNames . NavBar ] : ( request ) => { return this . requiredResponse ( this . getNavigationBarItems ( request . arguments , true ) ) ; } , [ CommandNames . NavBarFull ] : ( request ) => { return this . requiredResponse ( this . getNavigationBarItems ( request . arguments , false ) ) ; } , [ CommandNames . NavTree ] : ( request ) => { return this . requiredResponse ( this . getNavigationTree ( request . arguments , true ) ) ; } , [ CommandNames . NavTreeFull ] : ( request ) => { return this . requiredResponse ( this . getNavigationTree ( request . arguments , false ) ) ; } , [ CommandNames . Occurrences ] : ( request ) => { return this . requiredResponse ( this . getOccurrences ( request . arguments ) ) ; } , [ CommandNames . DocumentHighlights ] : ( request ) => { return this . requiredResponse ( this . getDocumentHighlights ( request . arguments , true ) ) ; } , [ CommandNames . DocumentHighlightsFull ] : ( request ) => { return this . requiredResponse ( this . getDocumentHighlights ( request . arguments , false ) ) ; } , [ CommandNames . CompilerOptionsForInferredProjects ] : ( request ) => { this . setCompilerOptionsForInferredProjects ( request . arguments ) ; return this . requiredResponse ( true ) ; } , [ CommandNames . ProjectInfo ] : ( request ) => { return this . requiredResponse ( this . getProjectInfo ( request . arguments ) ) ; } , [ CommandNames . ReloadProjects ] : ( ) => { this . projectService . reloadProjects ( ) ; return this . notRequired ( ) ; } , [ CommandNames . JsxClosingTag ] : ( request ) => { return this . requiredResponse ( this . getJsxClosingTag ( request . arguments ) ) ; } , [ CommandNames . GetCodeFixes ] : ( request ) => { return this . requiredResponse ( this . getCodeFixes ( request . arguments , true ) ) ; } , [ CommandNames . GetCodeFixesFull ] : ( request ) => { return this . requiredResponse ( this . getCodeFixes ( request . arguments , false ) ) ; } , [ CommandNames . GetCombinedCodeFix ] : ( request ) => { return this . requiredResponse ( this . getCombinedCodeFix ( request . arguments , true ) ) ; } , [ CommandNames . GetCombinedCodeFixFull ] : ( request ) => { return this . requiredResponse ( this . getCombinedCodeFix ( request . arguments , false ) ) ; } , [ CommandNames . ApplyCodeActionCommand ] : ( request ) => { return this . requiredResponse ( this . applyCodeActionCommand ( request . arguments ) ) ; } , [ CommandNames . GetSupportedCodeFixes ] : ( ) => { return this . requiredResponse ( this . getSupportedCodeFixes ( ) ) ; } , [ CommandNames . GetApplicableRefactors ] : ( request ) => { return this . requiredResponse ( this . getApplicableRefactors ( request . arguments ) ) ; } , [ CommandNames . GetEditsForRefactor ] : ( request ) => { return this . requiredResponse ( this . getEditsForRefactor ( request . arguments , true ) ) ; } , [ CommandNames . GetEditsForRefactorFull ] : ( request ) => { return this . requiredResponse ( this . getEditsForRefactor ( request . arguments , false ) ) ; } , [ CommandNames . OrganizeImports ] : ( request ) => { return this . requiredResponse ( this . organizeImports ( request . arguments , true ) ) ; } , [ CommandNames . OrganizeImportsFull ] : ( request ) => { return this . requiredResponse ( this . organizeImports ( request . arguments , false ) ) ; } , [ CommandNames . GetEditsForFileRename ] : ( request ) => { return this . requiredResponse ( this . getEditsForFileRename ( request . arguments , true ) ) ; } , [ CommandNames . GetEditsForFileRenameFull ] : ( request ) => { return this . requiredResponse ( this . getEditsForFileRename ( request . arguments , false ) ) ; } , [ CommandNames . ConfigurePlugin ] : ( request ) => { this . configurePlugin ( request . arguments ) ; this . doOutput ( undefined , CommandNames . ConfigurePlugin , request . seq , true ) ; return this . notRequired ( ) ; } , [ CommandNames . SelectionRange ] : ( request ) => { return this . requiredResponse ( this . getSmartSelectionRange ( request . arguments , true ) ) ; } , [ CommandNames . SelectionRangeFull ] : ( request ) => { return this . requiredResponse ( this . getSmartSelectionRange ( request . arguments , false ) ) ; } , [ CommandNames . PrepareCallHierarchy ] : ( request ) => { return this . requiredResponse ( this . prepareCallHierarchy ( request . arguments ) ) ; } , [ CommandNames . ProvideCallHierarchyIncomingCalls ] : ( request ) => { return this . requiredResponse ( this . provideCallHierarchyIncomingCalls ( request . arguments ) ) ; } , [ CommandNames . ProvideCallHierarchyOutgoingCalls ] : ( request ) => { return this . requiredResponse ( this . provideCallHierarchyOutgoingCalls ( request . arguments ) ) ; } , [ CommandNames . ToggleLineComment ] : ( request ) => { return this . requiredResponse ( this . toggleLineComment ( request . arguments , true ) ) ; } , [ CommandNames . ToggleLineCommentFull ] : ( request ) => { return this . requiredResponse ( this . toggleLineComment ( request . arguments , false ) ) ; } , [ CommandNames . ToggleMultilineComment ] : ( request ) => { return this . requiredResponse ( this . toggleMultilineComment ( request . arguments , true ) ) ; } , [ CommandNames . ToggleMultilineCommentFull ] : ( request ) => { return this . requiredResponse ( this . toggleMultilineComment ( request . arguments , false ) ) ; } , [ CommandNames . CommentSelection ] : ( request ) => { return this . requiredResponse ( this . commentSelection ( request . arguments , true ) ) ; } , [ CommandNames . CommentSelectionFull ] : ( request ) => { return this . requiredResponse ( this . commentSelection ( request . arguments , false ) ) ; } , [ CommandNames . UncommentSelection ] : ( request ) => { return this . requiredResponse ( this . uncommentSelection ( request . arguments , true ) ) ; } , [ CommandNames . UncommentSelectionFull ] : ( request ) => { return this . requiredResponse ( this . uncommentSelection ( request . arguments , false ) ) ; } , [ CommandNames . ProvideInlayHints ] : ( request ) => { return this . requiredResponse ( this . provideInlayHints ( request . arguments ) ) ; } } ) ) ; public addProtocolHandler ( command , handler ) { if ( this . handlers . has ( command ) ) { throw new Error ( `Protocol handler already exists for command "${ command }"` ) ; } this . handlers . set ( command , handler ) ; } private setCurrentRequest ( requestId ) { Debug . assert ( this . currentRequestId === undefined ) ; this . currentRequestId = requestId ; this . cancellationToken . setRequest ( requestId ) ; } private resetCurrentRequest ( requestId ) { Debug . assert ( this . currentRequestId === requestId ) ; this . currentRequestId = undefined ! ; this . cancellationToken . resetRequest ( requestId ) ; } public executeWithRequestId < T > ( requestId , f ) { try { this . setCurrentRequest ( requestId ) ; return f ( ) ; } finally { this . resetCurrentRequest ( requestId ) ; } } public executeCommand ( request ) { const handler = this . handlers . get ( request . command ) ; if ( handler ) { return this . executeWithRequestId ( request . seq , ( ) => handler ( request ) ) ; } else { this . logger . msg ( `Unrecognized JSON command:${ stringifyIndented ( request ) }` , Msg . Err ) ; this . doOutput ( undefined , CommandNames . Unknown , request . seq , false , `Unrecognized JSON command: ${ request . command }` ) ; return { responseRequired : false } ; } } public onMessage ( message ) { this . gcTimer . scheduleCollect ( ) ; this . performanceData = undefined ; let start ; if ( this . logger . hasLevel ( LogLevel . requestTime ) ) { start = this . hrtime ( ) ; if ( this . logger . hasLevel ( LogLevel . verbose ) ) { this . logger . info ( `request:${ indent ( this . toStringMessage ( message ) ) }` ) ; } } let request ; let relevantFile ; try { request = this . parseMessage ( message ) ; relevantFile = request . arguments && ( request as protocol . FileRequest ) . arguments . file ? ( request as protocol . FileRequest ) . arguments : undefined ; tracing ?. instant ( tracing . Phase . Session , "request" , { seq : request . seq , command : request . command } ) ; perfLogger . logStartCommand ( "" + request . command , this . toStringMessage ( message ) . substring ( 0 , 100 ) ) ; tracing ?. push ( tracing . Phase . Session , "executeCommand" , { seq : request . seq , command : request . command } , true ) ; const { response , responseRequired } = this . executeCommand ( request ) ; tracing ?. pop ( ) ; if ( this . logger . hasLevel ( LogLevel . requestTime ) ) { const elapsedTime = hrTimeToMilliseconds ( this . hrtime ( start ) ) . toFixed ( 4 ) ; if ( responseRequired ) { this . logger . perftrc ( `${ request . seq }::${ request . command }: elapsed time (in milliseconds) ${ elapsedTime }` ) ; } else { this . logger . perftrc ( `${ request . seq }::${ request . command }: async elapsed time (in milliseconds) ${ elapsedTime }` ) ; } } perfLogger . logStopCommand ( "" + request . command , "Success" ) ; tracing ?. instant ( tracing . Phase . Session , "response" , { seq : request . seq , command : request . command , success : ! ! response } ) ; if ( response ) { this . doOutput ( response , request . command , request . seq , true ) ; } else if ( responseRequired ) { this . doOutput ( undefined , request . command , request . seq , false , "No content available." ) ; } } catch ( err ) { tracing ?. popAll ( ) ; if ( err instanceof OperationCanceledException ) { perfLogger . logStopCommand ( "" + ( request && request . command ) , "Canceled: " + err ) ; tracing ?. instant ( tracing . Phase . Session , "commandCanceled" , { seq : request ?. seq , command : request ?. command } ) ; this . doOutput ( { canceled : true } , request ! . command , request ! . seq , true ) ; return ; } this . logErrorWorker ( err , this . toStringMessage ( message ) , relevantFile ) ; perfLogger . logStopCommand ( "" + ( request && request . command ) , "Error: " + err ) ; tracing ?. instant ( tracing . Phase . Session , "commandError" , { seq : request ?. seq , command : request ?. command , message : ( err as Error ) . message } ) ; this . doOutput ( undefined , request ? request . command : CommandNames . Unknown , request ? request . seq : 0 , false , "Error processing request. " + ( err as StackTraceError ) . message + "\n" + ( err as StackTraceError ) . stack ) ; } } protected parseMessage ( message ) { return JSON . parse ( message as any as string ) as protocol . Request ; } protected toStringMessage ( message ) { return message as any as string ; } private getFormatOptions ( file ) { return this . projectService . getFormatCodeOptions ( file ) ; } private getPreferences ( file ) { return this . projectService . getPreferences ( file ) ; } private getHostFormatOptions ( ) { return this . projectService . getHostFormatCodeOptions ( ) ; } private getHostPreferences ( ) { return this . projectService . getHostPreferences ( ) ; } } interface FileAndProject { readonly file : NormalizedPath ; readonly project : Project ; } function toProtocolTextSpan ( textSpan , scriptInfo ) { return { start : scriptInfo . positionToLineOffset ( textSpan . start ) , end : scriptInfo . positionToLineOffset ( textSpanEnd ( textSpan ) ) } ; } function toProtocolTextSpanWithContext ( span , contextSpan , scriptInfo ) { const textSpan = toProtocolTextSpan ( span , scriptInfo ) ; const contextTextSpan = contextSpan && toProtocolTextSpan ( contextSpan , scriptInfo ) ; return contextTextSpan ? { ... textSpan , contextStart : contextTextSpan . start , contextEnd : contextTextSpan . end } : textSpan ; } function convertTextChangeToCodeEdit ( change , scriptInfo ) { return { start : positionToLineOffset ( scriptInfo , change . span . start ) , end : positionToLineOffset ( scriptInfo , textSpanEnd ( change . span ) ) , newText : change . newText } ; } function positionToLineOffset ( info , position ) { return isConfigFile ( info ) ? locationFromLineAndCharacter ( info . getLineAndCharacterOfPosition ( position ) ) : info . positionToLineOffset ( position ) ; } function locationFromLineAndCharacter ( lc ) { return { line : lc . line + 1 , offset : lc . character + 1 } ; } function convertNewFileTextChangeToCodeEdit ( textChanges ) { Debug . assert ( textChanges . textChanges . length === 1 ) ; const change = first ( textChanges . textChanges ) ; Debug . assert ( change . span . start === 0 && change . span . length === 0 ) ; return { fileName : textChanges . fileName , textChanges : [ { start : { line : 0 , offset : 0 } , end : { line : 0 , offset : 0 } , newText : change . newText } ] } ; } export interface HandlerResponse { response ? : { } ; responseRequired ? : boolean ; } export function getLocationInNewDocument ( oldText , renameFilename , renameLocation , edits ) { const newText = applyEdits ( oldText , renameFilename , edits ) ; const { line , character } = computeLineAndCharacterOfPosition ( computeLineStarts ( newText ) , renameLocation ) ; return { line : line + 1 , offset : character + 1 } ; } function applyEdits ( text , textFilename , edits ) { for ( const { fileName , textChanges } of edits ) { if ( fileName !== textFilename ) { continue ; } for ( let i = textChanges . length - 1 ; i >= 0 ; i -- ) { const { newText , span : { start , length } } = textChanges [ i ] ; text = text . slice ( 0 , start ) + newText + text . slice ( start + length ) ; } } return text ; } function referenceEntryToReferencesResponseItem ( projectService , { fileName , textSpan , contextSpan , isWriteAccess , isDefinition } ) { const scriptInfo = Debug . checkDefined ( projectService . getScriptInfo ( fileName ) ) ; const span = toProtocolTextSpanWithContext ( textSpan , contextSpan , scriptInfo ) ; const lineSpan = scriptInfo . lineToTextSpan ( span . start . line - 1 ) ; const lineText = scriptInfo . getSnapshot ( ) . getText ( lineSpan . start , textSpanEnd ( lineSpan ) ) . replace ( /\r|\n/g , "" ) ; return { file : fileName , ... span , lineText , isWriteAccess , isDefinition } ; } function isCompletionEntryData ( data ) { return data === undefined || data && typeof data === "object" && typeof data . exportName === "string" && ( data . fileName === undefined || typeof data . fileName === "string" ) && ( data . ambientModuleName === undefined || typeof data . ambientModuleName === "string" && ( data . isPackageJsonImport === undefined || typeof data . isPackageJsonImport === "boolean" ) ) ; } }