import { URI } from "vscode-uri" ; import * as ts from "typescript" ; import { Location , Range , DiagnosticSeverity , Diagnostic } from "vscode-languageserver" ; import * as fs from "fs" ; import * as path from "path" ; import { itemKind } from './utils' ; import { serializeArgumentName } from './ast-helpers' ; export function normalizeDefinitions ( results , root ) { return ( results || [ ] ) . map ( el => { return tsDefinitionToLocation ( el , root ) ; } ) . filter ( ( el ) => el !== null ) ; } const ignoreNames = [ 'willDestroy' , 'toString' ] ; export function normalizeCompletions ( tsResults , realPath , isArg ) { return ( tsResults ? tsResults . entries : [ ] ) . filter ( ( { name } ) => ! ignoreNames . includes ( name ) && ! name . startsWith ( "_t" ) && ! name . includes ( ' - ' ) && name !== 'globalScope' && name !== 'defaultYield' ) . map ( el => { return { label : isArg ? serializeArgumentName ( realPath ) + el . name : realPath + el . name , data : el . name , kind : itemKind ( el . kind ) } ; } ) ; } export function offsetToRange ( start , limit , source ) { let rLines = /(.*?(?:\r\n?|\n|$))/gm ; let startLine = source . slice ( 0 , start ) . match ( rLines ) || [ ] ; if ( ! source || startLine . length < 2 ) { return Range . create ( 0 , 0 , 0 , 0 ) ; } let line = startLine . length - 2 ; let col = startLine [ startLine . length - 2 ] . length ; let endLine = source . slice ( start , limit ) . match ( rLines ) || [ ] ; let endCol = col ; let endLineNumber = line ; if ( endLine . length === 1 ) { endCol = col + limit ; endLineNumber = line + endLine . length - 1 ; } else { endCol = endLine [ endLine . length - 1 ] . length ; } return Range . create ( line , col , endLineNumber , endCol ) ; } export function tsDefinitionToLocation ( el , root ) { let scope = el . textSpan ; let fullPath = path . resolve ( el . fileName ) ; if ( ! fs . existsSync ( fullPath ) ) { fullPath = path . resolve ( path . join ( root , el . fileName ) ) ; if ( ! fs . existsSync ( fullPath ) ) { return null ; } } let file = fs . readFileSync ( fullPath , "utf8" ) ; return Location . create ( URI . file ( fullPath ) . toString ( ) , offsetToRange ( scope . start , scope . length , file ) ) ; } function messageConverter ( msg ) { if ( msg . startsWith ( "The 'this' context of type 'this' is not assignable to method's 'this' of type 'null'." ) ) { return "Unable to find context, is file created and property defined?" ; } console . log ( msg ) ; return msg ; } function getSeverity ( msg ) { let severity = DiagnosticSeverity . Error ; if ( msg . startsWith ( "Object is possibly 'undefined'" ) ) { severity = DiagnosticSeverity . Warning ; } else if ( msg . startsWith ( "Object is possibly 'null'" ) ) { severity = DiagnosticSeverity . Warning ; } return severity ; } function toFullDiagnostic ( err ) { if ( ! err . file || err . start === undefined ) { return null ; } let preErrorText = err . file . text . slice ( 0 , err . start ) ; let postErrorText = err . file . text . slice ( err . start , err . file . text . length ) ; if ( err . start < err . file . text . indexOf ( '@mark-meaningful-issues-start' ) ) { return null ; } let closestLeftMark = postErrorText . indexOf ( '["' ) ; let closestRightMarkOffset = postErrorText . indexOf ( '"]' ) ; let maybeMark = err . file . text . slice ( closestLeftMark + err . start , closestRightMarkOffset + err . start ) ; let hasNewline = err . file . text . slice ( err . start , err . start + closestLeftMark ) . split ( '\n' ) . length > 1 ; maybeMark = maybeMark . slice ( maybeMark . indexOf ( '[' ) + 2 , maybeMark . indexOf ( ']' ) ) . trim ( ) . split ( ' - ' ) [ 0 ] ; let start , end ; if ( maybeMark . includes ( ':' ) && ! hasNewline ) { [ start , end ] = maybeMark . split ( ':' ) ; } else { let preError = preErrorText . slice ( preErrorText . lastIndexOf ( '//@mark' ) , preErrorText . length ) ; let mark = preError . slice ( preError . indexOf ( '[' ) + 1 , preError . indexOf ( ']' ) ) . trim ( ) ; [ start , end ] = mark . split ( ':' ) ; } if ( ! start || ! end ) { let postError = err . file . text . slice ( err . start , err . file . text . length ) ; let postErrorMark = postError . slice ( postError . indexOf ( '/*@path-mark ' ) + 13 , postError . indexOf ( '*/' ) ) ; [ start , end ] = postErrorMark . split ( ':' ) ; if ( ! start || ! end ) { console . log ( err ) ; return null ; } } let [ startCol , startRow ] = start . split ( ',' ) . map ( ( e ) => parseInt ( e , 10 ) ) ; let [ endCol , endRow ] = end . split ( ',' ) . map ( ( e ) => parseInt ( e , 10 ) ) ; let msgText = diagnosticToString ( err . messageText ) ; if ( msgText . startsWith ( "Object is of type 'unknown'" ) ) { return null ; } if ( msgText . startsWith ( "Type 'any' is not assignable to type 'never'" ) ) { return null ; } if ( msgText . startsWith ( "Property 'args' does not exist on type" ) ) { return null ; } if ( msgText . startsWith ( "Expected 0 arguments, but got 2." ) ) { return null ; } if ( msgText . startsWith ( "Cannot invoke an object which is possibly" ) ) { return null ; } return { severity : getSeverity ( msgText ) , range : Range . create ( startCol - 1 , startRow , endCol - 1 , endRow ) , message : messageConverter ( msgText ) , source : "typed-templates" } ; } function diagnosticToString ( message , indent = '' ) { if ( typeof message === 'string' ) { return `${ indent }${ message }` ; } else if ( message . next && message . next . length ) { let items = message . next . map ( ( msg ) => diagnosticToString ( msg , `${ indent }  ` ) ) ; return `${ indent }${ message . messageText }\n${ items . join ( '\n' ) }` ; } else { return `${ indent }${ message . messageText }` ; } } export function getFullSemanticDiagnostics ( service , fileName ) { const tsDiagnostics = service . getSemanticDiagnostics ( fileName ) ; const results = tsDiagnostics . map ( ( error ) => toFullDiagnostic ( error ) ) . filter ( ( el ) => el !== null ) ; const diagnostics = results as Diagnostic [ ] ; return diagnostics ; }