/******************************************************************************
 * 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 , Range , RenameParams , TextDocumentPositionParams , TextEdit , WorkspaceEdit } from 'vscode-languageserver' ; import { Position } from 'vscode-languageserver-textdocument' ; import { isNamed , NameProvider } from '../references/naming' ; import { References } from '../references/references' ; import { LangiumServices } from '../services' ; import { CstNode } from '../syntax-tree' ; import { findLeafNodeAtOffset } from '../utils/ast-util' ; import { MaybePromise } from '../utils/promise-util' ; import { LangiumDocument } from '../workspace/documents' ; import { ReferenceFinder } from './reference-finder' ; export interface RenameHandler { throws throws renameElement ( document , params , cancelToken ? ) : MaybePromise < WorkspaceEdit | undefined > ; throws throws prepareRename ( document , params , cancelToken ? ) : MaybePromise < Range | undefined > ; } export class DefaultRenameHandler implements RenameHandler { protected readonly referenceFinder : ReferenceFinder ; protected readonly references : References ; protected readonly nameProvider : NameProvider ; constructor ( services ) { this . referenceFinder = services . lsp . ReferenceFinder ; this . references = services . references . References ; this . nameProvider = services . references . NameProvider ; } async renameElement ( document , params ) { const changes = { } ; const references = await this . referenceFinder . findReferences ( document , { ... params , context : { includeDeclaration : true } } ) ; if ( ! Array . isArray ( references ) ) { return undefined ; } references . forEach ( location => { const change = TextEdit . replace ( location . range , params . newName ) ; if ( changes [ location . uri ] ) { changes [ location . uri ] . push ( change ) ; } else { changes [ location . uri ] = [ change ] ; } } ) ; return { changes } ; } prepareRename ( document , params ) { return this . renameNodeRange ( document , params . position ) ; } protected renameNodeRange ( doc , position ) { const rootNode = doc . parseResult . value . $cstNode ; const offset = doc . textDocument . offsetAt ( position ) ; if ( rootNode && offset ) { const leafNode = findLeafNodeAtOffset ( rootNode , offset ) ; if ( ! leafNode ) { return undefined ; } const isCrossRef = this . references . findDeclaration ( leafNode ) ; if ( isCrossRef || this . isNameNode ( leafNode ) ) { return leafNode . range ; } } return undefined ; } protected isNameNode ( leafNode ) { return leafNode ?. element && isNamed ( leafNode . element ) && leafNode === this . nameProvider . getNameNode ( leafNode . element ) ; } }