intellij-lsp/intellij-lsp-plugin

View on GitHub
src/main/scala/com/github/gtache/lsp/contributors/rename/LSPRenameHandler.scala

Summary

Maintainability
B
4 hrs
Test Coverage
F
0%
/**
 *     Copyright 2017-2018 Guillaume Tâche
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 */
package com.github.gtache.lsp.contributors.rename

import com.github.gtache.lsp.PluginMain
import com.github.gtache.lsp.contributors.psi.LSPPsiElement
import com.github.gtache.lsp.editor.EditorEventManager
import com.github.gtache.lsp.utils.FileUtils
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.openapi.actionSystem.{CommonDataKeys, DataContext}
import com.intellij.openapi.command.impl.StartMarkAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.{Messages, NonEmptyInputValidator}
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
import com.intellij.psi.{PsiElement, PsiFile, PsiNameIdentifierOwner, PsiNamedElement}
import com.intellij.refactoring.rename.inplace.{InplaceRefactoring, MemberInplaceRenameHandler, MemberInplaceRenamer}
import com.intellij.refactoring.rename.{PsiElementRenameHandler, RenameHandler, RenameHandlerRegistry, RenamePsiElementProcessor}

class LSPRenameHandler extends RenameHandler {
  override def invoke(project: Project, elements: Array[PsiElement], dataContext: DataContext): Unit = {
    if (elements.length == 1) new MemberInplaceRenameHandler().doRename(elements(0), dataContext.getData(CommonDataKeys.EDITOR), dataContext)
    else invoke(project, dataContext.getData(CommonDataKeys.EDITOR), dataContext.getData(CommonDataKeys.PSI_FILE), dataContext)
  }

  override def invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext): Unit = {
    EditorEventManager.forEditor(editor) match {
      case Some(m) =>
        if (editor.getContentComponent.hasFocus) {
          val psiElement = m.getElementAtOffset(editor.getCaretModel.getCurrentCaret.getOffset)
          if (psiElement != null) {
            doRename(psiElement, editor, dataContext)
          }
        }
      case None =>
    }
  }

  def doRename(elementToRename: PsiElement, editor: Editor, dataContext: DataContext): InplaceRefactoring = {
    elementToRename match {
      case owner: PsiNameIdentifierOwner =>
        val processor = RenamePsiElementProcessor.forElement(elementToRename)
        if (processor.isInplaceRenameSupported) {
          val startMarkAction = StartMarkAction.canStart(elementToRename.getProject)
          if (startMarkAction == null || (processor.substituteElementToRename(elementToRename, editor) eq elementToRename)) {
            processor.substituteElementToRename(elementToRename, editor, (element: PsiElement) => {
              val renamer = createMemberRenamer(element, owner, editor)
              val startedRename = renamer.performInplaceRename
              if (!startedRename) performDialogRename(elementToRename, editor, dataContext)
            })
            return null
          }
          else {
            val inplaceRefactoring = editor.getUserData(InplaceRefactoring.INPLACE_RENAMER)
            if (inplaceRefactoring != null && (inplaceRefactoring.getClass eq classOf[MemberInplaceRenamer])) {
              val templateState = TemplateManagerImpl.getTemplateState(InjectedLanguageUtil.getTopLevelEditor(editor))
              if (templateState != null) templateState.gotoEnd(true)
            }
          }
        }
      case _ =>
    }
    performDialogRename(elementToRename, editor, dataContext)
    null
  }

  protected def performDialogRename(elementToRename: PsiElement, editor: Editor, dataContext: DataContext): Unit = {
    EditorEventManager.forEditor(editor) match {
      case Some(manager) =>
        val renameTo = Messages.showInputDialog(editor.getProject, "Enter new name: ", "Rename", Messages.getQuestionIcon, "", new NonEmptyInputValidator())
        if (renameTo != null && renameTo != "") manager.rename(renameTo)
      case None =>
    }
  }

  def createMemberRenamer(element: PsiElement, elementToRename: PsiNameIdentifierOwner, editor: Editor): MemberInplaceRenamer = {
    new LSPInplaceRenamer(element.asInstanceOf[PsiNamedElement], elementToRename, editor)()
  }

  override def isRenaming(dataContext: DataContext): Boolean = isAvailableOnDataContext(dataContext)

  protected def checkAvailable(elementToRename: PsiElement, editor: Editor, dataContext: DataContext): Boolean = {
    if (!isAvailableOnDataContext(dataContext)) {
      RenameHandlerRegistry.getInstance.getRenameHandler(dataContext).invoke(elementToRename.getProject, editor, elementToRename.getContainingFile, dataContext)
      return false
    }
    true
  }

  override def isAvailableOnDataContext(dataContext: DataContext): Boolean = {
    val element = PsiElementRenameHandler.getElement(dataContext)
    val editor = CommonDataKeys.EDITOR.getData(dataContext)
    val file = CommonDataKeys.PSI_FILE.getData(dataContext)
    if (editor == null || file == null) return false
    isAvailable(element, editor, file)
  }

  def isAvailable(psiElement: PsiElement, editor: Editor, psiFile: PsiFile): Boolean = {
    psiElement match {
      case _: PsiFile => true
      case _: LSPPsiElement => true
      //IntelliJ 2018 returns psiElement null for unsupported languages
      case _ => psiElement == null && PluginMain.isExtensionSupported(FileUtils.extFromPsiFile(psiFile))
    }
  }

}