[diff] Enable history navigation through diffs

Currently limited only to those history points that are generated
by the text editors present in (some) diff viewers.

Also limited in the sense that diff previews do not change upon navigation.

GitOrigin-RevId: fffb97e2c12131c686f119af5eb1407956a282ee
This commit is contained in:
Chris Lemaire
2024-02-02 15:58:08 +01:00
committed by intellij-monorepo-bot
parent 5d3e39e290
commit 5377efd5c8
7 changed files with 125 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<registryKey defaultValue="false"
description="Try to include navigation within a diff in the editor's navigation history."
key="include.diffs.in.navigation.history"/>
</extensions>
</idea-plugin>

View File

@@ -25,6 +25,7 @@ abstract class DiffEditorBase(
) : FileEditorBase() {
companion object {
private val LOG = logger<DiffEditorBase>()
const val DIFF_IN_NAVIGATION_HISTORY_KEY = "include.diffs.in.navigation.history"
}
private val panel = MyPanel(component)

View File

@@ -6,9 +6,13 @@ import com.intellij.diff.impl.DiffRequestProcessorListener
import com.intellij.diff.tools.combined.editors
import com.intellij.diff.util.DiffUserDataKeysEx
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorState
import com.intellij.openapi.fileEditor.FileEditorStateLevel
import com.intellij.openapi.fileEditor.FileEditorWithTextEditors
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.fileEditor.impl.text.TextEditorState
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile
import javax.swing.JComponent
@@ -36,6 +40,20 @@ open class DiffRequestProcessorEditor(
super.dispose()
}
override fun getState(level: FileEditorStateLevel): FileEditorState {
if (!Registry.`is`(DIFF_IN_NAVIGATION_HISTORY_KEY)) {
return FileEditorState.INSTANCE
}
return processor.getState(level)
}
override fun setState(state: FileEditorState) {
if (!Registry.`is`(DIFF_IN_NAVIGATION_HISTORY_KEY) || state !is DiffRequestProcessorEditorState) return
processor.setState(state)
}
override fun getPreferredFocusedComponent(): JComponent? = processor.preferredFocusedComponent
override fun selectNotify() {
@@ -55,3 +73,13 @@ open class DiffRequestProcessorEditor(
return processor.activeViewer?.editors.orEmpty()
}
}
data class DiffRequestProcessorEditorState(
val embeddedEditorStates: List<TextEditorState>
) : FileEditorState {
override fun canBeMergedWith(otherState: FileEditorState, level: FileEditorStateLevel): Boolean =
otherState is DiffRequestProcessorEditorState &&
embeddedEditorStates.zip(otherState.embeddedEditorStates).all { (l, r) -> l.canBeMergedWith(r, level) }
override fun toString(): String = embeddedEditorStates.joinToString()
}

View File

@@ -7,6 +7,8 @@ import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.diff.*;
import com.intellij.diff.FrameDiffTool.DiffViewer;
import com.intellij.diff.actions.impl.*;
import com.intellij.diff.editor.DiffRequestProcessorEditor;
import com.intellij.diff.editor.DiffRequestProcessorEditorState;
import com.intellij.diff.impl.DiffSettingsHolder.DiffSettings;
import com.intellij.diff.impl.ui.DiffToolChooser;
import com.intellij.diff.lang.DiffIgnoredRangeProvider;
@@ -38,6 +40,9 @@ import com.intellij.openapi.diff.DiffBundle;
import com.intellij.openapi.diff.impl.DiffUsageTriggerCollector;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbAware;
@@ -678,6 +683,30 @@ public abstract class DiffRequestProcessor implements CheckedDisposable {
return myContext;
}
public void setState(@NotNull DiffRequestProcessorEditorState state) {
DiffViewer viewer = getActiveViewer();
if (!(viewer instanceof EditorDiffViewer)) return;
var editors = ((EditorDiffViewer)viewer).getEditors();
var editorStates = state.getEmbeddedEditorStates();
TextEditorProvider textEditorProvider = TextEditorProvider.getInstance();
for (int i = 0; i < Math.min(editorStates.size(), editors.size()); i++) {
textEditorProvider.setStateImpl(myProject, editors.get(i), editorStates.get(i), true);
}
}
public @NotNull FileEditorState getState(@NotNull FileEditorStateLevel level) {
DiffViewer viewer = getActiveViewer();
if (!(viewer instanceof EditorDiffViewer)) return FileEditorState.INSTANCE;
List<? extends Editor> editors = ((EditorDiffViewer)viewer).getEditors();
TextEditorProvider textEditorProvider = TextEditorProvider.getInstance();
return new DiffRequestProcessorEditorState(ContainerUtil.map(editors, (editor) ->
textEditorProvider.getStateImpl(null, editor, level)));
}
public @Nullable DiffViewer getActiveViewer() {
if (myState instanceof DefaultState) {
return ((DefaultState)myState).myViewer;

View File

@@ -11,6 +11,9 @@ import com.intellij.diff.util.DiffUserDataKeysEx
import com.intellij.diff.util.DiffUtil
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileEditor.FileEditorState
import com.intellij.openapi.fileEditor.FileEditorStateLevel
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.util.Disposer
import com.intellij.util.concurrency.annotations.RequiresEdt
import java.awt.Dimension
@@ -48,6 +51,31 @@ open class CombinedDiffComponentFactory(val model: CombinedDiffModel) {
internal fun getMainComponent() = mainUi.getComponent()
internal fun getState(level: FileEditorStateLevel): FileEditorState {
val viewer = combinedViewer
if (viewer == null) return FileEditorState.INSTANCE
val textEditorProvider = TextEditorProvider.getInstance()
return CombinedDiffEditorState(
model.requests.map { it.id }.toSet(),
viewer.getCurrentBlockId(),
viewer.getCurrentDiffViewer()?.editors?.map { textEditorProvider.getStateImpl(null, it, level) } ?: listOf()
)
}
internal fun setState(state: CombinedDiffEditorState) {
val viewer = combinedViewer ?: return
if (model.requests.map { it.id }.toSet() != state.currentBlockIds) return
val textEditorProvider = TextEditorProvider.getInstance()
state.activeEditorStates.zip(viewer.getDiffViewerForId(state.activeBlockId)?.editors ?: listOf()) { st, editor ->
textEditorProvider.setStateImpl(null, editor, st, true)
}
viewer.selectDiffBlock(state.activeBlockId, true)
viewer.scrollToCaret()
}
private fun createMainUI(): CombinedDiffMainUI {
return CombinedDiffMainUI(model, createGoToChangeAction()).also { ui ->
Disposer.register(ourDisposable, ui)

View File

@@ -3,8 +3,12 @@ package com.intellij.diff.tools.combined
import com.intellij.diff.editor.DiffEditorBase
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorState
import com.intellij.openapi.fileEditor.FileEditorStateLevel
import com.intellij.openapi.fileEditor.FileEditorWithTextEditors
import com.intellij.openapi.fileEditor.impl.text.TextEditorState
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.registry.Registry
import javax.swing.JComponent
class CombinedDiffEditor(file: CombinedDiffVirtualFile, val factory: CombinedDiffComponentFactory) :
@@ -15,9 +19,34 @@ class CombinedDiffEditor(file: CombinedDiffVirtualFile, val factory: CombinedDif
super.dispose()
}
override fun getState(level: FileEditorStateLevel): FileEditorState {
if (!Registry.`is`(DIFF_IN_NAVIGATION_HISTORY_KEY)) return FileEditorState.INSTANCE
return factory.getState(level)
}
override fun setState(state: FileEditorState) {
if (state !is CombinedDiffEditorState) return
factory.setState(state)
}
override fun getPreferredFocusedComponent(): JComponent? = factory.getPreferredFocusedComponent()
override fun getEmbeddedEditors(): List<Editor> {
return factory.model.context.getUserData(COMBINED_DIFF_VIEWER_KEY)?.editors.orEmpty()
}
}
data class CombinedDiffEditorState(
val currentBlockIds: Set<CombinedBlockId>,
val activeBlockId: CombinedBlockId,
val activeEditorStates: List<TextEditorState>
) : FileEditorState {
override fun canBeMergedWith(otherState: FileEditorState, level: FileEditorStateLevel): Boolean {
return otherState is CombinedDiffEditorState &&
(currentBlockIds != otherState.currentBlockIds ||
(activeBlockId == otherState.activeBlockId &&
activeEditorStates.zip(otherState.activeEditorStates).all { (l, r) -> l.canBeMergedWith(r, level) } ))
}
}

View File

@@ -148,6 +148,9 @@
<applicationInitializedListener implementation="com.intellij.openapi.fileTypes.impl.associate.OSFileAssociationStartupConfigurator"/>
</extensions>
<xi:include href="/META-INF/diff-impl.xml">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/VCS.xml">
<xi:fallback/>
</xi:include>