import * as Sqlite from 'better-sqlite3' ; import * as lsp from 'vscode-languageserver' ; import { Database , UriTransformer } from './database' ; import { Id , EdgeLabels , DefinitionResult , FoldingRangeResult , DocumentSymbolResult , RangeBasedDocumentSymbol , Range , HoverResult , ReferenceResult , ItemEdgeProperties , DeclarationResult , Moniker , MonikerKind , VertexLabels , Vertex , Source } from 'lsif-protocol' ; import { MetaData , CompressorDescription , CompressionKind } from './protocol.compress' ; import { DocumentInfo } from './files' ; import { URI } from 'vscode-uri' ; interface DecompressorPropertyDescription { name : string ; index : number ; compressionKind : CompressionKind ; longForm ? : Map < string | number , string > ; } class Decompressor { public static all : Map < number , Decompressor > = new Map ( ) ; public static get ( id ) { return this . all . get ( id ) ; } private id : number ; private parentId : number | undefined ; private parent : Decompressor | undefined ; private properties : DecompressorPropertyDescription [ ] ; constructor ( description ) { this . id = description . id ; this . parentId = description . parent ; this . properties = [ ] ; for ( let item of description . properties ) { let propertyDescription = { name : item . name , index : item . index , compressionKind : item . compressionKind , longForm : undefined } ; if ( item . shortForm !== undefined ) { propertyDescription . longForm = new Map ( ) ; for ( let element of item . shortForm ) { propertyDescription . longForm . set ( element [ 1 ] , element [ 0 ] ) ; } } this . properties . push ( propertyDescription ) ; } Decompressor . all . set ( this . id , this ) ; } public link ( ) { if ( this . parentId !== undefined ) { this . parent = Decompressor . get ( this . parentId ) ; } } public getPropertyDescription ( name ) { for ( let item of this . properties ) { if ( item . name === name ) { return item ; } } return undefined ; } public decompress < T = object > ( compressed ) { let result = this . parent !== undefined ? this . parent . decompress ( compressed ) : Object . create ( null ) ; for ( let property of this . properties ) { let index = property . index ; let value = compressed [ index ] ; if ( value === null || value === undefined ) { continue ; } let decompressor ; switch ( property . compressionKind ) { case CompressionKind . id : result [ property . name ] = value ; break ; case CompressionKind . ids : result [ property . name ] = value ; break ; case CompressionKind . raw : result [ property . name ] = value ; break ; case CompressionKind . scalar : let convertedScalar = value ; if ( property . longForm !== undefined ) { let long = property . longForm . get ( value ) ; if ( long !== undefined ) { convertedScalar = long ; } } let dotIndex = property . name . indexOf ( '.' ) ; if ( dotIndex !== - 1 ) { let container = property . name . substr ( 0 , dotIndex ) ; let name = property . name . substring ( dotIndex + 1 ) ; if ( result [ container ] === undefined ) { result [ container ] = Object . create ( null ) ; } result [ container ] [ name ] = convertedScalar ; } else { result [ property . name ] = convertedScalar ; } break ; case CompressionKind . literal : if ( ! Array . isArray ( value ) || typeof value [ 0 ] !== 'number' ) { throw new Error ( `Compression kind literal detected on non array value. The property is ${ property . name }` ) ; } let convertedLiteral ; decompressor = Decompressor . get ( value [ 0 ] ) ; if ( decompressor === undefined ) { throw new Error ( `No decompression found for property ${ property . name } and id ${ value [ 0 ] }` ) ; } convertedLiteral = decompressor . decompress ( value ) ; result [ property . name ] = convertedLiteral ; break ; case CompressionKind . array : if ( ! Array . isArray ( value ) ) { throw new Error ( `Compression kind array detected on non array value. The property is ${ property . name }` ) ; } let convertedArray = [ ] ; for ( let element of value ) { let type = typeof element ; if ( type === 'string' || type === 'number' || type === 'boolean' ) { convertedArray . push ( element ) ; } else if ( Array . isArray ( element ) && element . length > 0 && typeof element [ 0 ] === 'number' ) { decompressor = Decompressor . get ( element [ 0 ] ) ; if ( decompressor === undefined ) { throw new Error ( `No decompression found for property ${ property . name } and id ${ element [ 0 ] }` ) ; } convertedArray . push ( decompressor . decompress ( element ) ) ; } else { throw new Error ( `The array element is neither a scalar nor an array.` ) ; } } result [ property . name ] = convertedArray ; break ; case CompressionKind . any : let convertedAny ; let type = typeof value ; if ( type === 'string' || type === 'number' || type === 'boolean' ) { convertedAny = value ; } else if ( Array . isArray ( value ) ) { convertedAny = [ ] ; for ( let element of value ) { let type = typeof element ; if ( type === 'string' || type === 'number' || type === 'boolean' ) { ( convertedAny as any [ ] ) . push ( element ) ; } else if ( Array . isArray ( element ) && element . length > 0 && typeof element [ 0 ] === 'number' ) { decompressor = Decompressor . get ( element [ 0 ] ) ; if ( decompressor === undefined ) { throw new Error ( `No decompression found for property ${ property . name } and id ${ element [ 0 ] }` ) ; } ( convertedAny as any [ ] ) . push ( decompressor . decompress ( element ) ) ; } else { throw new Error ( `The array element is neither a scalar nor an array.` ) ; } } } if ( convertedAny === undefined ) { throw new Error ( `Comression kind any can't be handled for property ${ property . name }. Value is ${ JSON . stringify ( value ) }` ) ; } result [ property . name ] = convertedAny ; break ; default : throw new Error ( `Compression kind ${ property . compressionKind } unknown.` ) ; } } return result ; } } interface RangeResult { id : number ; belongsTo : number ; startLine : number ; startCharacter : number ; endLine : number ; endCharacter : number ; } interface MetaDataResult { id : number ; value : string ; } interface IdResult { id : Id ; } interface LocationResult extends IdResult { uri : string ; startLine : number ; startCharacter : number ; endLine : number ; endCharacter : number ; } interface LocationResultWithProperty extends LocationResult { property : number ; } interface DocumentResult extends IdResult { label : number ; value : string ; } interface VertexResult extends IdResult { label : number ; value : string ; } interface ContentResult extends IdResult { content : string ; } interface NextResult { inV : number ; } interface PreviousResult { outV : number ; } interface DocumentInfoResult extends IdResult { projectId : Id ; uri : string ; documentHash : string ; } abstract class Retriever < T extends IdResult > { private values : Id [ ] ; public constructor ( private name , private db , private batchSize ) { this . values = [ ] ; } public clear ( ) { this . values = [ ] ; } public get isEmpty ( ) : boolean { return this . values . length === 0 ; } public add ( id ) { this . values . push ( id ) ; } public addMany ( ids ) { this . values . push ( ... ids ) ; } public run ( ) { let result = new Array ( this . values . length ) ; let batch = [ ] ; let mapping = new Map ( ) ; for ( let i = 0 ; i < this . values . length ; i ++ ) { let value = this . values [ i ] ; batch . push ( value ) ; mapping . set ( value , i ) ; if ( batch . length === this . batchSize ) { this . retrieveBatch ( result , batch , mapping ) ; batch = [ ] ; mapping . clear ( ) ; } } if ( batch . length > 0 ) { this . retrieveBatch ( result , batch , mapping ) ; } this . values = [ ] ; return result ; } private retrieveBatch ( result , batch , mapping ) { let stmt = batch . length === this . batchSize ? this . getFullStatement ( this . batchSize ) : this . getRestStatement ( batch . length ) ; let data = stmt . all ( batch ) ; if ( batch . length !== data . length ) { throw new Error ( `Couldn't retrieve all data for retriever ${ this . name }` ) ; } for ( let element of data ) { result [ mapping . get ( element . id ) ! ] = element ; } } protected prepare ( stmt , size ) { return this . db . prepare ( `${ stmt } (${ new Array ( size ) . fill ( '?' ) . join ( ',' ) })` ) ; } protected abstract getFullStatement ( size ) ; protected abstract getRestStatement ( size ) ; } class VertexRetriever extends Retriever < VertexResult > { private static statement : string = [ 'Select v.id, v.label, v.value from vertices v' , 'Where v.id in' ] . join ( ' ' ) ; private static preparedStatements : Map < number , Sqlite . Statement > = new Map ( ) ; public constructor ( db , batchSize = 16 ) { super ( 'VertexRetriever' , db , batchSize ) ; } protected getFullStatement ( size ) { let result = VertexRetriever . preparedStatements . get ( size ) ; if ( ! result ) { result = this . prepare ( VertexRetriever . statement , size ) ; VertexRetriever . preparedStatements . set ( size , result ) ; } return result ; } protected getRestStatement ( size ) { return this . prepare ( VertexRetriever . statement , size ) ; } } class LocationRetriever extends Retriever < LocationResult > { private static statement : string = [ 'Select r.id, r.startLine, r.startCharacter, r.endLine, r.endCharacter, d.uri from ranges r' , 'Inner Join documents d On r.belongsTo = d.id' , 'Where r.id in' ] . join ( ' ' ) ; private static preparedStatements : Map < number , Sqlite . Statement > = new Map ( ) ; public constructor ( db , batchSize = 16 ) { super ( 'LocationRetriever' , db , batchSize ) ; } protected getFullStatement ( size ) { let result = LocationRetriever . preparedStatements . get ( size ) ; if ( ! result ) { result = this . prepare ( LocationRetriever . statement , size ) ; LocationRetriever . preparedStatements . set ( size , result ) ; } return result ; } protected getRestStatement ( size ) { return this . prepare ( LocationRetriever . statement , size ) ; } } export class GraphStore extends Database { private db ! : Sqlite . Database ; private allDocumentsStmt ! : Sqlite . Statement ; private getDocumentContentStmt ! : Sqlite . Statement ; private findRangeStmt ! : Sqlite . Statement ; private findDocumentStmt ! : Sqlite . Statement ; private findResultStmt ! : Sqlite . Statement ; private findMonikerStmt ! : Sqlite . Statement ; private findMatchingMonikersStmt ! : Sqlite . Statement ; private findAttachedMonikersStmt ! : Sqlite . Statement ; private findNextMonikerStmt ! : Sqlite . Statement ; private findVertexIdForMonikerStmt ! : Sqlite . Statement ; private findNextVertexStmt ! : Sqlite . Statement ; private findPreviousVertexStmt ! : Sqlite . Statement ; private findResultForDocumentStmt ! : Sqlite . Statement ; private findRangeFromReferenceResult ! : Sqlite . Statement ; private findResultFromReferenceResult ! : Sqlite . Statement ; private findCascadesFromReferenceResult ! : Sqlite . Statement ; private findRangeFromResult ! : Sqlite . Statement ; private workspaceRoot ! : URI ; private vertexLabels : Map < string , number > | undefined ; private edgeLabels : Map < string , number > | undefined ; private itemEdgeProperties : Map < string , number > | undefined ; public constructor ( ) { super ( ) ; } public load ( file , transformerFactory ) { this . db = new Sqlite ( file , { readonly : true } ) ; this . readMetaData ( ) ; this . readSource ( ) ; this . allDocumentsStmt = this . db . prepare ( 'Select id, uri, documentHash From documents' ) ; this . getDocumentContentStmt = this . db . prepare ( 'Select content From contents Where documentHash = ?' ) ; this . findDocumentStmt = this . db . prepare ( 'Select id From documents Where uri = ?' ) ; this . findRangeStmt = this . db . prepare ( [ 'Select r.id, r.belongsTo, r.startLine, r.startCharacter, r.endline, r.endCharacter From ranges r' , 'Inner Join documents d On r.belongsTo = d.id' , 'where' , 'd.uri = $uri and (' , '(r.startLine < $line and $line < r.endline) or' , '(r.startLine = $line and r.startCharacter <= $character and $line < r.endline) or' , '(r.startLine < $line and r.endLine = $line and $character <= r.endCharacter) or' , '(r.startLine = $line and r.endLine = $line and r.startCharacter <= $character and $character <= r.endCharacter)' , ')' ] . join ( ' ' ) ) ; const nextLabel = this . edgeLabels !== undefined ? this . edgeLabels . get ( EdgeLabels . next ) ! : EdgeLabels . next ; const monikerEdgeLabel = this . edgeLabels !== undefined ? this . edgeLabels . get ( EdgeLabels . moniker ) ! : EdgeLabels . moniker ; const monikerAttachLabel = this . edgeLabels !== undefined ? this . edgeLabels . get ( EdgeLabels . attach ) ! : EdgeLabels . attach ; this . findResultStmt = this . db . prepare ( [ 'Select v.id, v.label, v.value From vertices v' , 'Inner Join edges e On e.inV = v.id' , 'Where e.outV = $source and e.label = $label' ] . join ( ' ' ) ) ; this . findMonikerStmt = this . db . prepare ( [ 'Select v.id, v.label, v.value From vertices v' , 'Inner Join edges e On e.inV = v.id' , `Where e.outV = $source and e.label = ${ monikerEdgeLabel }` ] . join ( ' ' ) ) ; this . findMatchingMonikersStmt = this . db . prepare ( [ 'Select v.id, v.label, v.value From vertices v' , 'Inner Join monikers m on v.id = m.id' , 'Where m.identifier = $identifier and m.scheme = $scheme and m.id != $exclude' ] . join ( ' ' ) ) ; this . findAttachedMonikersStmt = this . db . prepare ( [ 'Select v.id, v.label, v.value from vertices v' , 'Inner Join edges e On e.outV = v.id' , `Where e.inV = $source and e.label = ${ monikerAttachLabel }` ] . join ( ' ' ) ) ; this . findNextMonikerStmt = this . db . prepare ( [ 'Select e.inV From edges e' , `Where e.outV = $source and e.label = ${ monikerAttachLabel }` ] . join ( ' ' ) ) ; this . findVertexIdForMonikerStmt = this . db . prepare ( [ 'Select v.id from vertices v' , 'INNER Join edges e On v.id = e.outV' , `Where e.label = ${ monikerEdgeLabel } and e.inV = $id` ] . join ( ' ' ) ) ; this . findNextVertexStmt = this . db . prepare ( [ 'Select e.inV From edges e' , `Where e.outV = $source and e.label = ${ nextLabel }` ] . join ( ' ' ) ) ; this . findPreviousVertexStmt = this . db . prepare ( [ 'Select e.outV From edges e' , `Where e.inV = $source and e.label = ${ nextLabel }` ] . join ( ' ' ) ) ; this . findResultForDocumentStmt = this . db . prepare ( [ 'Select v.id, v.label, v.value from vertices v' , 'Inner Join edges e On e.inV = v.id' , 'Inner Join documents d On d.id = e.outV' , 'Where d.uri = $uri and e.label = $label' ] . join ( ' ' ) ) ; this . findRangeFromResult = this . db . prepare ( [ 'Select r.id, r.startLine, r.startCharacter, r.endLine, r.endCharacter, d.uri from ranges r' , 'Inner Join items i On i.inV = r.id' , 'Inner Join documents d On r.belongsTo = d.id' , 'Where i.outV = $id' ] . join ( ' ' ) ) ; this . findRangeFromReferenceResult = this . db . prepare ( [ 'Select r.id, r.startLine, r.startCharacter, r.endLine, r.endCharacter, i.property, d.uri from ranges r' , 'Inner Join items i On i.inV = r.id' , 'Inner Join documents d On r.belongsTo = d.id' , 'Where i.outV = $id and (i.property in (1, 2, 3))' ] . join ( ' ' ) ) ; this . findResultFromReferenceResult = this . db . prepare ( [ 'Select v.id, v.label, v.value from vertices v' , 'Inner Join items i On i.inV = v.id' , 'Where i.outV = $id and i.property = 4' ] . join ( ' ' ) ) ; this . findCascadesFromReferenceResult = this . db . prepare ( [ 'Select v.id, v.label, v.value from vertices v' , 'Inner Join items i On i.inV = v.id' , 'Where i.outV = $id and i.property = 5' ] . join ( ' ' ) ) ; this . initialize ( transformerFactory ) ; return Promise . resolve ( ) ; } private readMetaData ( ) { let result = this . db . prepare ( 'Select * from meta' ) . all ( ) ; if ( result === undefined || result . length !== 1 ) { throw new Error ( 'Failed to read meta data record.' ) ; } let metaData = JSON . parse ( result [ 0 ] . value ) ; if ( metaData . compressors !== undefined ) { this . vertexLabels = new Map ( ) ; this . edgeLabels = new Map ( ) ; this . itemEdgeProperties = new Map ( ) ; for ( let decription of metaData . compressors . all ) { new Decompressor ( decription ) ; } for ( let element of Decompressor . all . values ( ) ) { element . link ( ) ; } let decompressor = Decompressor . get ( metaData . compressors . vertexCompressor ) ; if ( decompressor === undefined ) { throw new Error ( 'No vertex decompressor found.' ) ; } let description = decompressor . getPropertyDescription ( 'label' ) ; if ( description === undefined || description . longForm === undefined ) { throw new Error ( 'No vertex label property description found.' ) ; } for ( let item of description . longForm ) { this . vertexLabels . set ( item [ 1 ] , item [ 0 ] as number ) ; } decompressor = Decompressor . get ( metaData . compressors . edgeCompressor ) ; if ( decompressor === undefined ) { throw new Error ( 'No edge decompressor found.' ) ; } description = decompressor . getPropertyDescription ( 'label' ) ; if ( description === undefined || description . longForm === undefined ) { throw new Error ( 'No edge label property description found.' ) ; } for ( let item of description . longForm ) { this . edgeLabels . set ( item [ 1 ] , item [ 0 ] as number ) ; } decompressor = Decompressor . get ( metaData . compressors . itemEdgeCompressor ) ; if ( decompressor === undefined ) { throw new Error ( 'No item edge decompressor found.' ) ; } description = decompressor . getPropertyDescription ( 'property' ) ; if ( description === undefined || description . longForm === undefined ) { throw new Error ( 'No item property description found.' ) ; } for ( let item of description . longForm ) { this . itemEdgeProperties . set ( item [ 1 ] , item [ 0 ] as number ) ; } } } private readSource ( ) { const sourceLabel = this . vertexLabels !== undefined ? this . vertexLabels . get ( VertexLabels . source ) : VertexLabels . source ; const source = this . decompress ( JSON . parse ( this . db . prepare ( `Select v.value from vertices v where v.label = ${ sourceLabel }` ) . get ( ) . value ) ) ; if ( source !== undefined ) { this . workspaceRoot = URI . parse ( source . workspaceRoot ) ; } } public getWorkspaceRoot ( ) { return this . workspaceRoot ; } public close ( ) { this . db . close ( ) ; } protected getDocumentInfos ( ) { let result = this . allDocumentsStmt . all ( ) ; if ( result === undefined ) { return [ ] ; } return result . map ( ( item ) => { return { id : item . id , uri : item . uri , hash : item . documentHash } ; } ) ; } protected findFile ( uri ) { let result = this . findDocumentStmt . get ( uri ) ; return result ; } protected fileContent ( info ) { let result = this . getDocumentContentStmt . get ( info . hash ) ; if ( ! result || ! result . content ) { return '' ; } return Buffer . from ( result . content ) . toString ( 'base64' ) ; } public foldingRanges ( uri ) { let foldingResult = this . getResultForDocument ( this . toDatabase ( uri ) , EdgeLabels . textDocument_foldingRange ) ; if ( foldingResult === undefined ) { return undefined ; } return foldingResult . result ; } public documentSymbols ( uri ) { let symbolResult = this . getResultForDocument ( this . toDatabase ( uri ) , EdgeLabels . textDocument_documentSymbol ) ; if ( symbolResult === undefined ) { return undefined ; } if ( symbolResult . result . length === 0 ) { return [ ] ; } if ( lsp . DocumentSymbol . is ( symbolResult . result [ 0 ] ) ) { return symbolResult . result as lsp . DocumentSymbol [ ] ; } else { const vertexRetriever = new VertexRetriever ( this . db , 16 ) ; let collectRanges = ( element ) => { vertexRetriever . add ( element . id ) ; if ( element . children ) { element . children . forEach ( collectRanges ) ; } } ; let convert = ( result , elements , ranges ) => { for ( let element of elements ) { let range = ranges . get ( element . id ) ; if ( range !== undefined ) { let symbol = this . asDocumentSymbol ( range ) ; if ( symbol ) { result . push ( symbol ) ; if ( element . children !== undefined && element . children . length > 0 ) { symbol . children = [ ] ; convert ( symbol . children , element . children , ranges ) ; } } } } } ; ( symbolResult . result as RangeBasedDocumentSymbol [ ] ) . forEach ( collectRanges ) ; let data = vertexRetriever . run ( ) ; let ranges = new Map ( ) ; for ( let element of data ) { let range = this . decompress ( JSON . parse ( element . value ) ) ; if ( range ) { ranges . set ( range . id , range ) ; } } let result = [ ] ; convert ( result , symbolResult . result as RangeBasedDocumentSymbol [ ] , ranges ) ; return result ; } } public hover ( uri , position ) { const ranges = this . findRange ( this . toDatabase ( uri ) , position ) ; if ( ranges === undefined ) { return undefined ; } const findHover = ( range ) : lsp . Hover | undefined => { const [ hoverResult , anchorId ] = this . getResultForId ( range . id , EdgeLabels . textDocument_hover ) ; if ( hoverResult === undefined || hoverResult . result === undefined ) { return undefined ; } const result = Object . assign ( Object . create ( null ) , hoverResult . result ) ; if ( result . range === undefined ) { result . range = { start : { line : range . startLine , character : range . startCharacter } , end : { line : range . endLine , character : range . endCharacter } } ; } return result ; } ; let result ; for ( const range of ranges ) { result = findHover ( range ) ; if ( result !== undefined ) { break ; } } if ( result === undefined ) { return undefined ; } if ( Array . isArray ( result . contents ) ) { for ( let i = 0 ; i < result . contents . length ; ) { const elem = result . contents [ i ] ; if ( typeof elem !== 'string' && elem . language === undefined && elem . value === undefined ) { result . contents . splice ( i , 1 ) ; } else { i ++ ; } } } return result ; } public declarations ( uri , position ) { const ranges = this . findRange ( this . toDatabase ( uri ) , position ) ; if ( ranges === undefined ) { return undefined ; } const findDeclaration = ( range ) : lsp . Location | lsp . Location [ ] | undefined => { const [ declarationResult ] = this . getResultForId ( range . id , EdgeLabels . textDocument_declaration ) ; if ( declarationResult === undefined ) { return undefined ; } const result = [ ] ; const queryResult = this . findRangeFromResult . all ( { id : declarationResult . id } ) ; if ( queryResult && queryResult . length > 0 ) { for ( let item of queryResult ) { result . push ( this . createLocation ( item ) ) ; } } return result ; } ; for ( const range of ranges ) { const result = findDeclaration ( range ) ; if ( result !== undefined ) { return result ; } } return undefined ; } public definitions ( uri , position ) { const ranges = this . findRange ( this . toDatabase ( uri ) , position ) ; if ( ranges === undefined ) { return undefined ; } const findDefinitions = ( range ) : lsp . Location | lsp . Location [ ] | undefined => { const [ definitionResult ] = this . getResultForId ( range . id , EdgeLabels . textDocument_definition ) ; if ( definitionResult === undefined ) { return undefined ; } const result = [ ] ; const queryResult = this . findRangeFromResult . all ( { id : definitionResult . id } ) ; if ( queryResult && queryResult . length > 0 ) { for ( let item of queryResult ) { result . push ( this . createLocation ( item ) ) ; } } return result ; } ; for ( const range of ranges ) { const result = findDefinitions ( range ) ; if ( result !== undefined ) { return result ; } } return undefined ; } public references ( uri , position , context ) { const ranges = this . findRange ( this . toDatabase ( uri ) , position ) ; if ( ranges === undefined ) { return undefined ; } const result = [ ] ; const monikers = new Map ( ) ; const dedupRanges = new Set < Id > ( ) ; const findReferences = ( result , dedupRanges , monikers , range ) : void => { const [ referenceResult , anchorId ] = this . getResultForId ( range . id , EdgeLabels . textDocument_references ) ; if ( referenceResult === undefined ) { return undefined ; } this . resolveReferenceResult ( result , dedupRanges , monikers , referenceResult , context ) ; this . findMonikersForVertex ( monikers , anchorId ) ; for ( const moniker of monikers . values ( ) ) { if ( moniker . kind === MonikerKind . local ) { continue ; } const matchingMonikers = this . findMatchingMonikers ( moniker ) ; for ( const matchingMoniker of matchingMonikers ) { const vertexId = this . findVertexIdForMoniker ( matchingMoniker ) ; if ( vertexId === undefined ) { continue ; } const [ referenceResult ] = this . getResultForId ( vertexId , EdgeLabels . textDocument_references ) ; if ( referenceResult === undefined ) { continue ; } this . resolveReferenceResult ( result , dedupRanges , monikers , referenceResult , context ) ; } } } ; for ( const range of ranges ) { findReferences ( result , dedupRanges , monikers , range ) ; } return result ; } private resolveReferenceResult ( result , dedupRanges , monikers , referenceResult , context ) { const qr = this . findRangeFromReferenceResult . all ( { id : referenceResult . id } ) ; if ( qr && qr . length > 0 ) { const refLabel = this . getItemEdgeProperty ( ItemEdgeProperties . references ) ; for ( const item of qr ) { if ( item . property === refLabel || context . includeDeclaration && ! dedupRanges . has ( item . id ) ) { dedupRanges . add ( item . id ) ; result . push ( this . createLocation ( item ) ) ; } } } const mr = this . findCascadesFromReferenceResult . all ( { id : referenceResult . id } ) ; if ( mr ) { for ( const moniker of mr ) { if ( ! monikers . has ( moniker . id ) ) { monikers . set ( moniker . id , this . decompress ( JSON . parse ( moniker . value ) ) ) ; } } } const rqr = this . findResultFromReferenceResult . all ( { id : referenceResult . id } ) ; if ( rqr && rqr . length > 0 ) { for ( const item of rqr ) { this . resolveReferenceResult ( result , dedupRanges , monikers , this . decompress ( JSON . parse ( item . value ) ) , context ) ; } } } private findMonikersForVertex ( monikers , id ) { let currentId = id ; let moniker ; do { moniker = this . findMonikerStmt . get ( { source : currentId } ) ; if ( moniker !== undefined ) { break ; } const previous = this . findPreviousVertexStmt . get ( { source : currentId } ) ; if ( previous === undefined ) { moniker = undefined ; break ; } currentId = previous . outV ; } while ( currentId !== undefined ) ; if ( moniker === undefined ) { return ; } const result = [ this . decompress ( JSON . parse ( moniker . value ) ) ] ; for ( const moniker of result ) { monikers . set ( moniker . id , moniker ) ; const attachedMonikersResult = this . findAttachedMonikersStmt . all ( { source : moniker . id } ) ; for ( const attachedMonikerResult of attachedMonikersResult ) { const attachedMoniker = this . decompress ( JSON . parse ( attachedMonikerResult . value ) ) ; monikers . set ( attachedMoniker . id , attachedMoniker ) ; } } } private findMatchingMonikers ( moniker ) { const results = this . findMatchingMonikersStmt . all ( { identifier : moniker . identifier , scheme : moniker . scheme , exclude : moniker . id } ) ; return results . map ( vertex => this . decompress ( JSON . parse ( vertex . value ) ) ) ; } private findVertexIdForMoniker ( moniker ) { let currentId = moniker . id ; do { const next = this . findNextMonikerStmt . get ( { source : currentId } ) ; if ( next === undefined ) { break ; } currentId = next . inV ; } while ( currentId !== undefined ) ; if ( currentId === undefined ) { return ; } const result = this . findVertexIdForMonikerStmt . get ( { id : currentId } ) ; return result !== undefined ? result . id : undefined ; } private findRange ( uri , position ) { let dbResult = this . findRangeStmt . all ( { uri : uri , line : position . line , character : position . character } ) ; if ( dbResult === undefined || dbResult . length === 0 ) { return undefined ; } function sameRange ( a , b ) { return a . startLine === b . startLine && a . startCharacter === b . startCharacter && a . endLine === b . endLine && a . endCharacter === b . endCharacter ; } const result = [ ] ; const last = dbResult [ dbResult . length - 1 ] ; const belongsTo = new Set ( ) ; result . push ( last ) ; belongsTo . add ( last . belongsTo ) ; for ( let i = result . length - 2 ; i >= 0 ; i -- ) { const candidate = dbResult [ i ] ; if ( ! belongsTo . has ( candidate . belongsTo ) && sameRange ( last , candidate ) ) { result . push ( candidate ) ; } else { break ; } } return result ; } private getResultForId ( id , label ) ; private getResultForId ( id , label ) ; private getResultForId ( id , label ) ; private getResultForId ( id , label ) ; private getResultForId ( id , label ) { let currentId = id ; let result ; do { result = this . findResultStmt . get ( { source : currentId , label : this . getEdgeLabel ( label ) } ) ; if ( result !== undefined ) { break ; } const next = this . findNextVertexStmt . get ( { source : currentId } ) ; if ( next === undefined ) { result = undefined ; break ; } currentId = next . inV ; } while ( currentId !== undefined ) ; if ( result === undefined ) { return [ undefined , currentId ] ; } return [ this . decompress ( JSON . parse ( result . value ) ) , currentId ] ; } private getEdgeLabel ( label ) { if ( this . edgeLabels === undefined ) { return label ; } let result = this . edgeLabels . get ( label ) ; return result !== undefined ? result : label ; } private getItemEdgeProperty ( prop ) { if ( this . itemEdgeProperties === undefined ) { return prop ; } let result = this . itemEdgeProperties . get ( prop ) ; return result !== undefined ? result : prop ; } private asLocations ( values ) { let mapping = new Map ( ) ; let ids = [ ] ; let result = new Array ( values . length ) ; for ( let i = 0 ; i < values . length ; i ++ ) { let element = values [ i ] ; if ( lsp . Location . is ( element ) ) { result [ i ] = element ; } else { mapping . set ( element , i ) ; ids . push ( element ) ; } } if ( ids . length > 0 ) { const locationRetriever = new LocationRetriever ( this . db ) ; locationRetriever . addMany ( ids ) ; let data = locationRetriever . run ( ) ; for ( let element of data ) { result [ mapping . get ( element . id ) ! ] = this . createLocation ( element ) ; } } return result ; } private asLocation ( value ) { if ( lsp . Location . is ( value ) ) { return { range : value . range , uri : this . fromDatabase ( value . uri ) } ; } else { const locationRetriever = new LocationRetriever ( this . db , 1 ) ; locationRetriever . add ( value ) ; let data = locationRetriever . run ( ) [ 0 ] ; return this . createLocation ( data ) ; } } private createLocation ( data ) { return lsp . Location . create ( this . fromDatabase ( data . uri ) , lsp . Range . create ( data . startLine , data . startCharacter , data . endLine , data . endCharacter ) ) ; } private getResultForDocument ( uri , label ) ; private getResultForDocument ( uri , label ) ; private getResultForDocument ( uri , label ) { let data = this . findResultForDocumentStmt . get ( { uri , label : this . getEdgeLabel ( label ) } ) ; if ( data === undefined ) { return undefined ; } return this . decompress ( JSON . parse ( data . value ) ) ; } private decompress ( value ) { if ( Array . isArray ( value ) ) { let decompressor = Decompressor . get ( value [ 0 ] ) ; if ( decompressor ) { return decompressor . decompress ( value ) ; } } return value ; } }