intentions preview - use mock editor with document and caret model only

GitOrigin-RevId: 064d575ea687c9f6f54378344bf3c3724892c7df
This commit is contained in:
Dmitry.Krasilschikov
2019-12-03 16:04:11 +00:00
committed by intellij-monorepo-bot
parent cc018c2456
commit 968d8bbcba
5 changed files with 191 additions and 85 deletions
@@ -328,11 +328,16 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass {
}
}
if (indicator != null) {
~ if (indicator != null) {
indicator.setText("Searching for additional intention actions & quick fixes");
}
for (IntentionMenuContributor extension : IntentionMenuContributor.EP_NAME.getExtensionList()) {
extension.collectActions(hostEditor, hostFile, intentions, passIdToShowIntentionsFor, offset);
try {
extension.collectActions(hostEditor, hostFile, intentions, passIdToShowIntentionsFor, offset);
}
catch (UnsupportedOperationException e) {
//can collect action on a mock memory editor and produce exceptions - ignore
}
}
}
@@ -150,6 +150,10 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
catch (IndexNotReadyException e) {
return false;
}
catch (Exception e) {
//check action availability can be invoked on a mock editor and may produce exceptions
return false;
}
return true;
}
@@ -158,24 +162,29 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
@NotNull Editor hostEditor,
@Nullable PsiFile injectedFile,
@NotNull PairProcessor<? super PsiFile, ? super Editor> predicate) {
Editor editorToApply = null;
PsiFile fileToApply = null;
try {
Editor editorToApply = null;
PsiFile fileToApply = null;
Editor injectedEditor = null;
if (injectedFile != null) {
injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
if (predicate.process(injectedFile, injectedEditor)) {
editorToApply = injectedEditor;
fileToApply = injectedFile;
Editor injectedEditor = null;
if (injectedFile != null) {
injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
if (predicate.process(injectedFile, injectedEditor)) {
editorToApply = injectedEditor;
fileToApply = injectedFile;
}
}
}
if (editorToApply == null && hostEditor != injectedEditor && predicate.process(hostFile, hostEditor)) {
editorToApply = hostEditor;
fileToApply = hostFile;
if (editorToApply == null && hostEditor != injectedEditor && predicate.process(hostFile, hostEditor)) {
editorToApply = hostEditor;
fileToApply = hostFile;
}
if (editorToApply == null) return null;
return Pair.create(fileToApply, editorToApply);
}
catch (UnsupportedOperationException e) {
return null;
}
if (editorToApply == null) return null;
return Pair.create(fileToApply, editorToApply);
}
public static boolean chooseActionAndInvoke(@NotNull PsiFile hostFile,
@@ -10,10 +10,9 @@ import com.intellij.diff.comparison.ComparisonManager
import com.intellij.diff.comparison.ComparisonPolicy
import com.intellij.diff.fragments.LineFragment
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.progress.DumbProgressIndicator
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressManager
@@ -22,40 +21,48 @@ import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.ThrowableComputable
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
class IntentionPreviewComputable(private val project: Project,
private val action: IntentionAction,
private val originalFile: PsiFile,
private val originalEditor: Editor,
private val psiFileCopy: PsiFile,
private val editorCopy: Editor
) : Computable<Pair<PsiFile?, List<LineFragment>>?> {
private val originalEditor: Editor) : Computable<Pair<PsiFile?, List<LineFragment>>?> {
override fun compute(): Pair<PsiFile?, List<LineFragment>>? {
CommandProcessor.getInstance().runUndoTransparentAction(Runnable {
try {
ApplicationManager.getApplication().runReadAction(ThrowableComputable<Unit, Exception> {
val actionWithTextCachingCopy = intentionActionWithTextCaching(editorCopy, psiFileCopy)
if (actionWithTextCachingCopy == null) return@ThrowableComputable
val psiFileCopy = runReadAction { nonPhysicalPsiCopy(originalFile, project) }
ProgressManager.checkCanceled()
try {
originalEditor.document.setReadOnly(true)
val fileEditorPair = ShowIntentionActionsHandler.chooseFileForAction(psiFileCopy, editorCopy, actionWithTextCachingCopy.action)
?: return@ThrowableComputable
ProgressManager.checkCanceled()
actionWithTextCachingCopy.action.invoke(project, fileEditorPair.second, fileEditorPair.first)
ProgressManager.checkCanceled()
}
finally {
originalEditor.document.setReadOnly(false)
ApplicationManager.getApplication().invokeLater(Runnable { EditorFactory.getInstance().releaseEditor(editorCopy) })
}
})
}
catch (e: Exception) {
LOG.warn("There are exceptions on invocation the intention: '${action.text}' on a copy of the file.")
throw ProcessCanceledException()
}
})
val editorCopy = try {
IntentionPreviewEditor(psiFileCopy, runReadAction { originalEditor.caretModel.offset })
}
catch (e: IllegalStateException) {
LOG.warn(e)
throw ProcessCanceledException()
}
try {
ApplicationManager.getApplication().runReadAction(ThrowableComputable<Unit, Exception> {
val action = (intentionActionWithTextCaching(editorCopy, psiFileCopy) ?: return@ThrowableComputable).action
val fileEditorPair = ShowIntentionActionsHandler.chooseFileForAction(psiFileCopy, editorCopy, action)
?: return@ThrowableComputable
try {
originalEditor.document.setReadOnly(true)
ProgressManager.checkCanceled()
action.invoke(project, fileEditorPair.second, fileEditorPair.first)
ProgressManager.checkCanceled()
}
finally {
originalEditor.document.setReadOnly(false)
}
})
}
catch (e: UnsupportedOperationException) {
throw ProcessCanceledException()
}
catch (e: Exception) {
LOG.debug("There are exceptions on invocation the intention: '${action.text}' on a copy of the file.", e)
throw ProcessCanceledException()
}
return Pair<PsiFile?, List<LineFragment>>(
psiFileCopy,
@@ -65,13 +72,19 @@ class IntentionPreviewComputable(private val project: Project,
}))
}
private fun intentionActionWithTextCaching(editorCopy: Editor,
psiFileCopy: PsiFile): IntentionActionWithTextCaching? {
private fun intentionActionWithTextCaching(editorCopy: Editor, psiFileCopy: PsiFile): IntentionActionWithTextCaching? {
val actionsToShow = ShowIntentionsPass.getActionsToShow(editorCopy, psiFileCopy, false)
val cachedIntentions = CachedIntentions.createAndUpdateActions(project, psiFileCopy, editorCopy, actionsToShow)
return getFixes(cachedIntentions).find { it.text == action.text }
}
private fun nonPhysicalPsiCopy(psiFile: PsiFile, project: Project): PsiFile {
ProgressManager.checkCanceled()
return PsiFileFactory.getInstance(project).createFileFromText(psiFile.name,
psiFile.language,
psiFile.text, false, true, false,
psiFile.virtualFile)
}
fun getFixes(cachedIntentions: CachedIntentions): Sequence<IntentionActionWithTextCaching> =
sequenceOf<IntentionActionWithTextCaching>()
@@ -0,0 +1,114 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl.preview
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.editor.*
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.openapi.editor.event.CaretListener
import com.intellij.openapi.editor.event.EditorMouseEventArea
import com.intellij.openapi.editor.event.EditorMouseListener
import com.intellij.openapi.editor.event.EditorMouseMotionListener
import com.intellij.openapi.editor.markup.MarkupModel
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.psi.PsiFile
import java.awt.Insets
import java.awt.Point
import java.awt.event.MouseEvent
import java.awt.geom.Point2D
import javax.swing.JComponent
import javax.swing.border.Border
class IntentionPreviewEditor(private val psiFileCopy: PsiFile, private var caretOffset: Int) : UserDataHolderBase(), Editor {
private val document: Document = runReadAction { FileDocumentManager.getInstance().getDocument(psiFileCopy.viewProvider.virtualFile) }
?: throw IllegalStateException("Document should be not null.")
companion object {
const val UNSUPPORTED_MESSAGE: String = "It's unexpected to invoke this method on an intention preview calculating."
}
private val caretModel = IntentionPreviewCaretModel()
override fun getDocument(): Document = document
override fun getCaretModel() = caretModel
override fun isViewer(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getComponent(): JComponent = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getContentComponent(): JComponent = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun setBorder(border: Border?) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getInsets(): Insets = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getMarkupModel(): MarkupModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getScrollingModel(): ScrollingModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getSoftWrapModel(): SoftWrapModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getSettings(): EditorSettings = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getColorsScheme(): EditorColorsScheme = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getLineHeight(): Int = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun logicalPositionToXY(pos: LogicalPosition): Point = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun logicalPositionToOffset(pos: LogicalPosition): Int = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun logicalToVisualPosition(logicalPos: LogicalPosition): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun visualPositionToXY(visible: VisualPosition): Point = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun visualPositionToPoint2D(pos: VisualPosition): Point2D = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun visualToLogicalPosition(visiblePos: VisualPosition): LogicalPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun offsetToLogicalPosition(offset: Int): LogicalPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun offsetToVisualPosition(offset: Int): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun offsetToVisualPosition(offset: Int, leanForward: Boolean, beforeSoftWrap: Boolean): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getFoldingModel(): FoldingModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun xyToLogicalPosition(p: Point): LogicalPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun xyToVisualPosition(p: Point): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun xyToVisualPosition(p: Point2D): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addEditorMouseListener(listener: EditorMouseListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun removeEditorMouseListener(listener: EditorMouseListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addEditorMouseMotionListener(listener: EditorMouseMotionListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun removeEditorMouseMotionListener(listener: EditorMouseMotionListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun isDisposed(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getProject(): Project? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun isInsertMode(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getSelectionModel(): SelectionModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun isColumnMode(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun isOneLineMode(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getGutter(): EditorGutter = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getMouseEventArea(e: MouseEvent): EditorMouseEventArea? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun setHeaderComponent(header: JComponent?) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun hasHeaderComponent(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getHeaderComponent(): JComponent? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getIndentsModel(): IndentsModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getInlayModel(): InlayModel = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getEditorKind(): EditorKind = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
inner class IntentionPreviewCaretModel : CaretModel {
override fun moveToOffset(offset: Int) { caretOffset = offset }
override fun getOffset(): Int = caretOffset
override fun moveCaretRelatively(columnShift: Int, lineShift: Int, withSelection: Boolean, blockSelection: Boolean, scrollToCaret: Boolean) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun moveToLogicalPosition(pos: LogicalPosition) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun moveToVisualPosition(pos: VisualPosition) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun moveToOffset(offset: Int, locateBeforeSoftWrap: Boolean) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun isUpToDate(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getLogicalPosition(): LogicalPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getVisualPosition(): VisualPosition = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addCaretListener(listener: CaretListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun removeCaretListener(listener: CaretListener) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getVisualLineStart(): Int = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getVisualLineEnd(): Int = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getTextAttributes(): TextAttributes = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun supportsMultipleCarets(): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getCurrentCaret(): Caret = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getPrimaryCaret(): Caret = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getCaretCount(): Int = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getAllCarets(): MutableList<Caret> = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getCaretAt(pos: VisualPosition): Caret? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addCaret(pos: VisualPosition): Caret? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addCaret(pos: VisualPosition, makePrimary: Boolean): Caret? = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun removeCaret(caret: Caret): Boolean = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun removeSecondaryCarets() = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun setCaretsAndSelections(caretStates: MutableList<out CaretState>) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun setCaretsAndSelections(caretStates: MutableList<out CaretState>, updateSystemSelection: Boolean) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun getCaretsAndSelections(): MutableList<CaretState> = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun runForEachCaret(action: CaretAction) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun runForEachCaret(action: CaretAction, reverseOrder: Boolean) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun addCaretActionListener(listener: CaretActionListener, disposable: Disposable) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
override fun runBatchCaretOperation(runnable: Runnable) = throw UnsupportedOperationException(UNSUPPORTED_MESSAGE)
}
}
@@ -3,18 +3,14 @@ package com.intellij.codeInsight.intention.impl.preview
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.impl.CachedIntentions
import com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching
import com.intellij.codeInsight.intention.impl.IntentionHintComponent
import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComponent.Companion.LOADING_PREVIEW
import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComponent.Companion.NO_PREVIEW
import com.intellij.diff.fragments.LineFragment
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
@@ -22,9 +18,7 @@ import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.Computable
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.ui.popup.PopupPositionManager
import com.intellij.ui.popup.PopupUpdateProcessor
import com.intellij.util.Alarm
@@ -105,25 +99,11 @@ class IntentionPreviewPopupUpdateProcessor(private val project: Project,
fun start() {
component.startLoading()
ProgressManager.checkCanceled()
val psiFileCopy = ApplicationManager.getApplication().runReadAction(Computable<PsiFile> { nonPhysicalPsiCopy(originalFile, project) })
ProgressManager.checkCanceled()
val documentCopy = ApplicationManager.getApplication().runReadAction(
Computable<Document> { FileDocumentManager.getInstance().getDocument(psiFileCopy.viewProvider.virtualFile) })
if (documentCopy == null) {
select(NO_PREVIEW)
return
}
val editorCopy = EditorFactory.getInstance().createEditor(documentCopy, project)
.also { it.caretModel.moveToOffset(originalEditor.caretModel.offset) }
computation = ApplicationManager.getApplication().executeOnPooledThread(
Callable {
try {
ProgressManager.getInstance().runProcess(
IntentionPreviewComputable(project, action, originalFile, originalEditor, psiFileCopy, editorCopy), progressIndicator)
ProgressManager.getInstance().runProcess(IntentionPreviewComputable(project, action, originalFile, originalEditor),
progressIndicator)
}
catch (e: ProcessCanceledException) {
Pair<PsiFile?, List<LineFragment>>(null, emptyList())
@@ -155,21 +135,6 @@ class IntentionPreviewPopupUpdateProcessor(private val project: Project,
}
}
private fun nonPhysicalPsiCopy(psiFile: PsiFile, project: Project): PsiFile {
ProgressManager.checkCanceled()
return PsiFileFactory.getInstance(project).createFileFromText(psiFile.name,
psiFile.language,
psiFile.text, false, true, false,
psiFile.virtualFile)
}
fun getFixes(cachedIntentions: CachedIntentions): Sequence<IntentionActionWithTextCaching> =
sequenceOf<IntentionActionWithTextCaching>()
.plus(cachedIntentions.intentions)
.plus(cachedIntentions.inspectionFixes)
.plus(cachedIntentions.errorFixes)
fun toggleShow() {
show = !show
}