/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/ import { CancellationToken , Location , Range , ReferenceParams } from 'vscode-languageserver' ; import { URI } from 'vscode-uri' ; import { NameProvider } from '../references/naming' ; import { References } from '../references/references' ; import { LangiumServices } from '../services' ; import { AstNode , CstNode } from '../syntax-tree' ; import { findLeafNodeAtOffset , getDocument , isReference } from '../utils/ast-util' ; import { flatten } from '../utils/cst-util' ; import { MaybePromise } from '../utils/promise-util' ; import { LangiumDocument } from '../workspace/documents' ; export interface ReferenceFinder { throws throws findReferences ( document , params , cancelToken ? ) : MaybePromise < Location [ ] > ; } export class DefaultReferenceFinder implements ReferenceFinder { protected readonly nameProvider : NameProvider ; protected readonly references : References ; constructor ( services ) { this . nameProvider = services . references . NameProvider ; this . references = services . references . References ; } findReferences ( document , params ) { const rootNode = document . parseResult . value . $cstNode ; if ( ! rootNode ) { return [ ] ; } const refs = [ ] ; const selectedNode = findLeafNodeAtOffset ( rootNode , document . textDocument . offsetAt ( params . position ) ) ; if ( ! selectedNode ) { return [ ] ; } const targetAstNode = this . references . findDeclaration ( selectedNode ) ?. element ; if ( targetAstNode ) { if ( params . context . includeDeclaration ) { const declDoc = getDocument ( targetAstNode ) ; const nameNode = this . findNameNode ( targetAstNode , selectedNode . text ) ; if ( nameNode ) refs . push ( { docUri : declDoc . uri , range : nameNode . range } ) ; } this . references . findReferences ( targetAstNode ) . forEach ( reference => { if ( isReference ( reference ) ) { refs . push ( { docUri : document . uri , range : reference . $refNode . range } ) ; } else { const range = reference . segment . range ; refs . push ( { docUri : reference . sourceUri , range } ) ; } } ) ; } return refs . map ( ref => Location . create ( ref . docUri . toString ( ) , ref . range ) ) ; } protected findNameNode ( node , name ) { const nameNode = this . nameProvider . getNameNode ( node ) ; if ( nameNode ) return nameNode ; if ( node . $cstNode ) { const leafNode = flatten ( node . $cstNode ) . find ( ( n ) => n . text === name ) ; if ( leafNode ) return leafNode ; } return node . $cstNode ; } }