IJPL-159717 cleanup

GitOrigin-RevId: 70d2749a2941a58bba6e19c665ba8a68571e84c4
This commit is contained in:
Vladimir Krivosheev
2024-08-06 13:57:08 +02:00
committed by intellij-monorepo-bot
parent 19ac4816c2
commit 8d48ecc862
10 changed files with 298 additions and 349 deletions

View File

@@ -354,7 +354,6 @@ a:com.intellij.diff.editor.DiffVirtualFile
- createViewer(com.intellij.openapi.project.Project):com.intellij.diff.impl.DiffEditorViewer
a:com.intellij.diff.editor.DiffVirtualFileBase
- com.intellij.diff.editor.DiffContentVirtualFile
- com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$OptionallyIncluded
- com.intellij.openapi.vfs.VirtualFileWithoutContent
- com.intellij.testFramework.LightVirtualFile
- sf:Companion:com.intellij.diff.editor.DiffVirtualFileBase$Companion

View File

@@ -1,10 +1,11 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.projectView.impl
import com.intellij.ide.projectView.ProjectViewNode
import com.intellij.ide.projectView.impl.nodes.getVirtualFileForNodeOrItsPSI
import com.intellij.ide.util.treeView.AbstractTreeNode
import com.intellij.ide.util.treeView.InplaceCommentAppender
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
@@ -31,25 +32,27 @@ private fun ProjectViewNode<*>.shouldTryToShowInplaceComments(): Boolean {
return true
}
val parentNode = parent ?: return false // It's the root? We don't even show it, so don't bother with further checks.
return parentNode.cantBeFile() // All members belong to files.
return cantBeFile(parentNode) // All members belong to files.
}
private fun Any?.cantBePsiMember(): Boolean =
this == null ||
this is PsiFileSystemItem || // The node itself is either a file or directory, not a member.
this !is PsiElement // All members inherit from PsiElement, so it's something else (e.g. a plain text file).
private fun Any?.cantBePsiMember(): Boolean {
return this == null ||
this is PsiFileSystemItem || // The node itself is either a file or directory, not a member.
this !is PsiElement // All members inherit from PsiElement, so it's something else (e.g. a plain text file).
}
private fun <T> AbstractTreeNode<T>.cantBeFile(): Boolean =
isDirectory() || // Either we know for sure it's a directory,
!isFile() // or we know for sure it's not a file (could be neither, e.g. a package in Packages View).
private fun <T> cantBeFile(node: AbstractTreeNode<T>): Boolean {
return isDirectory(node) || // Either we know for sure it's a directory,
!isFile(node) // or we know for sure it's not a file (could be neither, e.g. a package in Packages View).
}
private fun <T> AbstractTreeNode<T>.isDirectory(): Boolean {
val value = this.value
private fun <T> isDirectory(node: AbstractTreeNode<T>): Boolean {
val value = node.value
// The ProjectFileNode check is for Scope-based views that don't use PsiDirectory.
return value is PsiDirectory || (value is ProjectFileNode && value.virtualFile.isDirectory)
}
private fun <T> AbstractTreeNode<T>.isFile(): Boolean = getVirtualFileForNodeOrItsPSI()?.isFile == true
private fun <T> isFile(node: AbstractTreeNode<T>): Boolean = node.getVirtualFileForNodeOrItsPSI()?.isFile == true
// To be used in Rider once it migrates from legacy logic, don't change the signature and/or visibility.
fun appendInplaceComments(appender: InplaceCommentAppender, project: Project?, file: VirtualFile?) {
@@ -62,6 +65,6 @@ fun appendInplaceComments(appender: InplaceCommentAppender, project: Project?, f
}
if (Registry.`is`("show.last.visited.timestamps") && file != null && project != null) {
IdeDocumentHistoryImpl.appendTimestamp(project, appender, file)
(IdeDocumentHistory.getInstance(project) as IdeDocumentHistoryImpl).appendTimestamp(appender, file)
}
}

View File

@@ -14295,24 +14295,17 @@ a:com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
- a:back():V
- a:clearHistory():V
- a:forward():V
- a:getBackPlaces():java.util.List
- a:getChangePlaces():java.util.List
- a:getChangedFiles():java.util.List
- s:getInstance(com.intellij.openapi.project.Project):com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
- a:gotoPlaceInfo(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- a:gotoPlaceInfo(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,Z):V
- a:includeCurrentCommandAsNavigation():V
- a:includeCurrentPlaceAsChangePlace():V
- a:isBackAvailable():Z
- a:isForwardAvailable():Z
- a:isNavigateNextChangeAvailable():Z
- a:isNavigatePreviousChangeAvailable():Z
- a:isSame(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):Z
- a:navigateNextChange():V
- a:navigatePreviousChange():V
- *a:reallyExcludeCurrentCommandAsNavigation():V
- a:removeBackPlace(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- a:removeChangePlace(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- a:setCurrentCommandHasMoves():V
a:com.intellij.openapi.fileEditor.impl.BaseRemoteFileEditor
- com.intellij.openapi.fileEditor.TextEditor
@@ -14628,61 +14621,6 @@ f:com.intellij.openapi.fileEditor.impl.HTMLEditorProvider$Request
f:com.intellij.openapi.fileEditor.impl.HTMLEditorProvider$Request$Companion
- f:html(java.lang.String):com.intellij.openapi.fileEditor.impl.HTMLEditorProvider$Request
- f:url(java.lang.String):com.intellij.openapi.fileEditor.impl.HTMLEditorProvider$Request
c:com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl
- com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
- com.intellij.openapi.Disposable
- com.intellij.openapi.components.PersistentStateComponent
- <init>(com.intellij.openapi.project.Project):V
- s:appendTimestamp(com.intellij.openapi.project.Project,com.intellij.ide.util.treeView.InplaceCommentAppender,com.intellij.openapi.vfs.VirtualFile):V
- f:back():V
- f:clearHistory():V
- p:createPlaceInfo(com.intellij.openapi.fileEditor.FileEditor,com.intellij.openapi.fileEditor.FileEditorProvider):com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo
- f:dispose():V
- p:executeCommand(java.lang.Runnable,java.lang.String,java.lang.Object):V
- f:forward():V
- getBackPlaces():java.util.List
- getChangePlaces():java.util.List
- getChangedFiles():java.util.List
- p:getFileEditorManager():com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
- p:getSelectedEditor():com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
- getState():java.lang.Object
- gotoPlaceInfo(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- gotoPlaceInfo(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,Z):V
- f:includeCurrentCommandAsNavigation():V
- f:includeCurrentPlaceAsChangePlace():V
- f:isBackAvailable():Z
- f:isForwardAvailable():Z
- isNavigateNextChangeAvailable():Z
- f:isNavigatePreviousChangeAvailable():Z
- isSame(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):Z
- loadState(java.lang.Object):V
- navigateNextChange():V
- f:navigatePreviousChange():V
- f:onSelectionChanged():V
- reallyExcludeCurrentCommandAsNavigation():V
- removeBackPlace(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- removeChangePlace(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo):V
- setCurrentCommandHasMoves():V
com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$OptionallyIncluded
- a:isIncludedInDocumentHistory(com.intellij.openapi.project.Project):Z
f:com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo
- <init>(com.intellij.openapi.vfs.VirtualFile,com.intellij.openapi.fileEditor.FileEditorState,java.lang.String,com.intellij.openapi.fileEditor.impl.EditorWindow,com.intellij.openapi.editor.RangeMarker):V
- <init>(com.intellij.openapi.vfs.VirtualFile,com.intellij.openapi.fileEditor.FileEditorState,java.lang.String,com.intellij.openapi.fileEditor.impl.EditorWindow,Z,com.intellij.openapi.editor.RangeMarker,J):V
- getCaretPosition():com.intellij.openapi.editor.RangeMarker
- getEditorTypeId():java.lang.String
- getFile():com.intellij.openapi.vfs.VirtualFile
- getNavigationState():com.intellij.openapi.fileEditor.FileEditorState
- getTimeStamp():J
- getWindow():com.intellij.openapi.fileEditor.impl.EditorWindow
- isPreviewTab():Z
com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$RecentPlacesListener
- sf:TOPIC:com.intellij.util.messages.Topic
- a:recentPlaceAdded(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,Z):V
- recentPlaceAdded(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,Z,java.lang.Object):V
- a:recentPlaceRemoved(com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$PlaceInfo,Z):V
com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$SkipFromDocumentHistory
- com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl$OptionallyIncluded
- isIncludedInDocumentHistory(com.intellij.openapi.project.Project):Z
c:com.intellij.openapi.fileEditor.impl.IdeUiServiceImpl
- com.intellij.ide.ui.IdeUiService
- <init>():V

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.actions;
import com.intellij.featureStatistics.FeatureUsageTracker;
@@ -36,6 +36,7 @@ import com.intellij.ui.speedSearch.SpeedSearchSupply;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import kotlin.Unit;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -87,7 +88,12 @@ public final class RecentLocationsAction extends DumbAwareAction implements Ligh
@NotNull @NlsContexts.StatusText String emptyText,
@Nullable Function<? super Boolean, ? extends List<IdeDocumentHistoryImpl.PlaceInfo>> supplier,
@Nullable Consumer<? super List<IdeDocumentHistoryImpl.PlaceInfo>> remover) {
RecentLocationsDataModel model = new RecentLocationsDataModel(project, supplier, remover);
RecentLocationsDataModel model = new RecentLocationsDataModel(project,
supplier == null ? null : supplier::apply,
remover == null ? null : infos -> {
remover.accept(infos);
return Unit.INSTANCE;
});
JBList<RecentLocationItem> list = new JBList<>(JBList.createDefaultListModel(model.getPlaces(showChanged)));
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(list,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,

View File

@@ -15,18 +15,16 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.DocumentUtil
import com.intellij.util.concurrency.SynchronizedClearableLazy
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import java.util.function.Consumer
import java.util.function.Function
import kotlin.math.max
import kotlin.math.min
@ApiStatus.Internal
internal class RecentLocationsDataModel(val project: Project,
private val placesSupplier: Function<in Boolean, out List<IdeDocumentHistoryImpl.PlaceInfo>>?,
private val placesRemover: Consumer<in List<IdeDocumentHistoryImpl.PlaceInfo>>?) {
internal class RecentLocationsDataModel(
private val project: Project,
private val placesSupplier: Function<Boolean, List<IdeDocumentHistoryImpl.PlaceInfo>>?,
private val placesRemover: ((List<IdeDocumentHistoryImpl.PlaceInfo>) -> Unit)?,
) {
private val navigationPlaces: SynchronizedClearableLazy<List<RecentLocationItem>> = calculateItems(project, false)
private val changedPlaces: SynchronizedClearableLazy<List<RecentLocationItem>> = calculateItems(project, true)
@@ -56,14 +54,14 @@ internal class RecentLocationsDataModel(val project: Project,
@Nls
private fun getBreadcrumbs(project: Project, placeInfo: IdeDocumentHistoryImpl.PlaceInfo): String {
val rangeMarker = placeInfo.getCaretPosition()
val fileName = placeInfo.getFile().name
val rangeMarker = placeInfo.caretPosition
val fileName = placeInfo.file.name
if (rangeMarker == null) {
return fileName
}
val collector = FileBreadcrumbsCollector.findBreadcrumbsCollector(project, placeInfo.getFile()) ?: return fileName
val crumbs = collector.computeCrumbs(placeInfo.getFile(), rangeMarker.document, rangeMarker.startOffset, true)
val collector = FileBreadcrumbsCollector.findBreadcrumbsCollector(project, placeInfo.file) ?: return fileName
val crumbs = collector.computeCrumbs(placeInfo.file, rangeMarker.document, rangeMarker.startOffset, true)
if (!crumbs.iterator().hasNext()) {
return fileName
@@ -106,10 +104,11 @@ internal class RecentLocationsDataModel(val project: Project,
}
private fun newLocationItem(place: IdeDocumentHistoryImpl.PlaceInfo): RecentLocationItem? {
val positionOffset = place.getCaretPosition()
val positionOffset = place.caretPosition
if (positionOffset == null || !positionOffset.isValid) {
return null
}
assert(positionOffset.startOffset == positionOffset.endOffset)
val fileDocument = positionOffset.document
@@ -123,21 +122,32 @@ internal class RecentLocationsDataModel(val project: Project,
}
fun removeItems(project: Project, isChanged: Boolean, items: List<RecentLocationItem>) {
if (isChanged) changedPlaces.drop() else navigationPlaces.drop()
if (placesRemover != null) {
placesRemover.accept(items.map { it.info })
if (isChanged) {
changedPlaces.drop()
}
else {
navigationPlaces.drop()
}
placesRemover?.let {
it(items.map { it.info })
return
}
val ideDocumentHistory = IdeDocumentHistory.getInstance(project)
for (item in items) {
(if (isChanged) {
for (it in if (isChanged) {
ideDocumentHistory.changePlaces
}
else {
ideDocumentHistory.backPlaces.filter { IdeDocumentHistory.getInstance(project).isSame(it, item.info) }
}).forEach {
if (isChanged) ideDocumentHistory.removeChangePlace(it)
else ideDocumentHistory.removeBackPlace(it)
}) {
if (isChanged) {
ideDocumentHistory.removeChangePlace(it)
}
else {
ideDocumentHistory.removeBackPlace(it)
}
}
}
}
@@ -185,10 +195,12 @@ internal class RecentLocationsDataModel(val project: Project,
val startOffset = document.getLineStartOffset(startLine)
val endOffset = document.getLineEndOffset(endLine)
return if (startOffset <= endOffset)
return if (startOffset <= endOffset) {
TextRange.create(startOffset, endOffset)
else
}
else {
DocumentUtil.getLineTextRange(document, line)
}
}
}
@@ -198,16 +210,19 @@ internal data class RecentLocationItem(
@JvmField val linesShift: Int,
@JvmField val ranges: Array<TextRange>
) {
override fun equals(other: Any?): Boolean = if (other !is RecentLocationItem) false
else {
info.getFile() == other.info.getFile() &&
linesShift == other.linesShift &&
text.length == other.text.length &&
ranges.size == other.ranges.size
override fun equals(other: Any?): Boolean {
if (other !is RecentLocationItem) {
return false
}
return info.file == other.info.file &&
linesShift == other.linesShift &&
text.length == other.text.length &&
ranges.size == other.ranges.size
}
override fun hashCode(): Int {
var result = info.getFile().hashCode()
var result = info.file.hashCode()
result = 31 * result + linesShift
result = 31 * result + text.length
result = 31 * result + ranges.size

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.fileEditor.ex;
import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl;
@@ -32,14 +32,20 @@ public abstract class IdeDocumentHistory {
public abstract @NotNull List<VirtualFile> getChangedFiles();
@ApiStatus.Internal
public abstract List<IdeDocumentHistoryImpl.PlaceInfo> getChangePlaces();
@ApiStatus.Internal
public abstract List<IdeDocumentHistoryImpl.PlaceInfo> getBackPlaces();
@ApiStatus.Internal
public abstract void removeChangePlace(@NotNull IdeDocumentHistoryImpl.PlaceInfo placeInfo);
@ApiStatus.Internal
public abstract void removeBackPlace(@NotNull IdeDocumentHistoryImpl.PlaceInfo placeInfo);
@ApiStatus.Internal
public abstract void gotoPlaceInfo(@NotNull IdeDocumentHistoryImpl.PlaceInfo info);
@ApiStatus.Internal
public abstract void gotoPlaceInfo(@NotNull IdeDocumentHistoryImpl.PlaceInfo info, boolean focusEditor);
@ApiStatus.Internal
@@ -54,5 +60,6 @@ public abstract class IdeDocumentHistory {
@ApiStatus.Experimental
public abstract void reallyExcludeCurrentCommandAsNavigation();
@ApiStatus.Internal
public abstract boolean isSame(@NotNull IdeDocumentHistoryImpl.PlaceInfo first, @NotNull IdeDocumentHistoryImpl.PlaceInfo second);
}

View File

@@ -1,4 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet")
package com.intellij.openapi.fileEditor.impl
import com.intellij.ide.ui.UISettings
@@ -36,7 +38,6 @@ import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.getProjectCachePath
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.ThrowableComputable
import com.intellij.openapi.util.registry.Registry.Companion.intValue
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
@@ -46,7 +47,6 @@ import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.psi.ExternalChangeAction
import com.intellij.reference.SoftReference
import com.intellij.testFramework.LightVirtualFile
import com.intellij.ui.SimpleTextAttributes
import com.intellij.util.concurrency.SynchronizedClearableLazy
@@ -64,47 +64,54 @@ import org.jetbrains.annotations.ApiStatus
import java.io.IOException
import java.lang.ref.Reference
import java.lang.ref.WeakReference
import java.nio.file.Path
import java.util.ArrayDeque
import java.util.ArrayList
import java.util.Deque
import java.util.HashSet
import java.util.function.Predicate
private val LOG = Logger.getInstance(IdeDocumentHistoryImpl::class.java)
private val BACK_QUEUE_LIMIT = intValue("editor.navigation.history.stack.size")
private val CHANGE_QUEUE_LIMIT = intValue("editor.navigation.history.stack.size")
@ApiStatus.Internal
@State(name = "IdeDocumentHistory", storages = [Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE)], reportStatistic = false)
open class IdeDocumentHistoryImpl(
private val project: Project,
coroutineScope: CoroutineScope,
) : IdeDocumentHistory(), Disposable, PersistentStateComponent<RecentlyChangedFilesState> {
private var myFileDocumentManager: FileDocumentManager? = null
private var fileDocumentManager: FileDocumentManager? = null
private val backPlaces: Deque<PlaceInfo> = ArrayDeque<PlaceInfo>()
private val forwardPlaces: Deque<PlaceInfo> = ArrayDeque<PlaceInfo>()
private var myBackInProgress = false
private val backPlaces = ArrayDeque<PlaceInfo>()
private val forwardPlaces = ArrayDeque<PlaceInfo>()
private var backInProgress = false
private var forwardInProgress = false
private var myCurrentCommandGroupId: Any? = null
private var lastGroupId: Reference<Any?>? = null // weak reference to avoid memory leaks when clients pass some exotic objects as commandId
private var currentCommandGroupId: Any? = null
// weak reference to avoid memory leaks when clients pass some exotic objects as commandId
private var lastGroupId: Reference<Any?>? = null
private var registeredBackPlaceInLastGroup = false
// change's navigation
private val changePlaces: Deque<PlaceInfo> = ArrayDeque<PlaceInfo>()
private val changePlaces = ArrayDeque<PlaceInfo>()
private var currentIndex = 0
private var commandStartPlace: PlaceInfo? = null
private var currentCommandIsNavigation = false
private var currentCommandHasChanges = false
private val myChangedFilesInCurrentCommand = HashSet<VirtualFile>()
private val changedFilesInCurrentCommand = HashSet<VirtualFile>()
private var currentCommandHasMoves = false
private var reallyExcludeCurrentCommandFromNavigation = false
private val recentFileTimestampMap: SynchronizedClearableLazy<PersistentHashMap<String?, Long?>?>
private val recentFileTimestampMap: SynchronizedClearableLazy<PersistentHashMap<String, Long>>
@Volatile
private var state = RecentlyChangedFilesState()
init {
val busConnection = project.getMessageBus().connect(this)
busConnection.subscribe<BulkFileListener>(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun after(events: MutableList<out VFileEvent>) {
val connection = project.getMessageBus().connect(coroutineScope)
connection.subscribe<BulkFileListener>(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun after(events: List<VFileEvent>) {
for (event in events) {
if (event is VFileDeleteEvent) {
removeInvalidFilesFromStacks()
@@ -113,42 +120,41 @@ open class IdeDocumentHistoryImpl(
}
}
})
busConnection.subscribe<CommandListener>(CommandListener.TOPIC, object : CommandListener {
connection.subscribe(CommandListener.TOPIC, object : CommandListener {
override fun commandStarted(event: CommandEvent) {
onCommandStarted(event.getCommandGroupId())
onCommandStarted(event.commandGroupId)
}
override fun commandFinished(event: CommandEvent) {
onCommandFinished(event.getProject(), event.getCommandGroupId())
onCommandFinished(event.project, event.commandGroupId)
}
})
val listener: EditorEventListener = object : EditorEventListener {
val listener = object : EditorEventListener {
override fun documentChanged(e: DocumentEvent) {
val document = e.getDocument()
val file = getFileDocumentManager().getFile(document)
if (file != null && file !is LightVirtualFile &&
!ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction::class.java)
) {
val file = getFileDocumentManager().getFile(e.document)
if (file != null &&
file !is LightVirtualFile &&
!ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction::class.java)) {
ThreadingAssertions.assertEventDispatchThread()
currentCommandHasChanges = true
myChangedFilesInCurrentCommand.add(file)
changedFilesInCurrentCommand.add(file)
}
}
override fun caretPositionChanged(e: CaretEvent) {
if (e.getOldPosition().line == e.getNewPosition().line) {
if (e.oldPosition.line == e.newPosition.line) {
return
}
val document = e.getEditor().getDocument()
val document = e.editor.getDocument()
if (getFileDocumentManager().getFile(document) != null) {
currentCommandHasMoves = true
}
}
}
recentFileTimestampMap = SynchronizedClearableLazy<PersistentHashMap<String?, Long?>?> { initRecentFilesTimestampMap(this.project) }
recentFileTimestampMap = SynchronizedClearableLazy { initRecentFilesTimestampMap(this.project) }
val multicaster = EditorFactory.getInstance().getEventMulticaster()
multicaster.addDocumentListener(listener, this)
@@ -157,72 +163,37 @@ open class IdeDocumentHistoryImpl(
FileEditorProvider.EP_FILE_EDITOR_PROVIDER.addExtensionPointListener(coroutineScope, object : ExtensionPointListener<FileEditorProvider> {
override fun extensionRemoved(provider: FileEditorProvider, pluginDescriptor: PluginDescriptor) {
val editorTypeId = provider.getEditorTypeId()
val clearStatePredicate = Predicate { e: PlaceInfo? -> editorTypeId == e!!.getEditorTypeId() }
val clearStatePredicate = Predicate { e: PlaceInfo -> editorTypeId == e.editorTypeId }
if (changePlaces.removeIf(clearStatePredicate)) {
currentIndex = changePlaces.size
}
backPlaces.removeIf(clearStatePredicate)
forwardPlaces.removeIf(clearStatePredicate)
if (commandStartPlace != null && commandStartPlace!!.getEditorTypeId() == editorTypeId) {
if (commandStartPlace?.editorTypeId == editorTypeId) {
commandStartPlace = null
}
}
})
}
companion object {
private val LOG = Logger.getInstance(IdeDocumentHistoryImpl::class.java)
private val BACK_QUEUE_LIMIT = intValue("editor.navigation.history.stack.size")
private val CHANGE_QUEUE_LIMIT = intValue("editor.navigation.history.stack.size")
private fun initRecentFilesTimestampMap(project: Project): PersistentHashMap<String?, Long?> {
val file = project.getProjectCachePath("recentFilesTimeStamps.dat")
try {
return IOUtil.openCleanOrResetBroken<PersistentHashMap<String?, Long?>>(ThrowableComputable { createMap(file) }, file)
}
catch (e: IOException) {
LOG.error("Cannot create PersistentHashMap in " + file, e)
throw RuntimeException(e)
}
fun appendTimestamp(appender: InplaceCommentAppender, file: VirtualFile) {
if (!UISettings.getInstance().showInplaceComments) {
return
}
@Throws(IOException::class)
private fun createMap(file: Path): PersistentHashMap<String?, Long?> {
return PersistentHashMap<String?, Long?>(file,
EnumeratorStringDescriptor.INSTANCE,
EnumeratorLongDescriptor,
256,
0,
StorageLockContext())
try {
val timestamp = recentFileTimestampMap.value.get(file.getPath())
if (timestamp != null) {
appender.append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES)
appender.append(DateFormatUtil.formatPrettyDateTime(timestamp), SimpleTextAttributes.GRAYED_SMALL_ATTRIBUTES)
}
}
fun appendTimestamp(
project: Project,
appender: InplaceCommentAppender,
file: VirtualFile
) {
if (!UISettings.getInstance().showInplaceComments) {
return
}
try {
val timestamp = (getInstance(project) as IdeDocumentHistoryImpl).recentFileTimestampMap.value!!.get(
file.getPath())
if (timestamp != null) {
appender.append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES)
appender.append(DateFormatUtil.formatPrettyDateTime(timestamp), SimpleTextAttributes.GRAYED_SMALL_ATTRIBUTES)
}
}
catch (e: IOException) {
LOG.info("Cannot get a timestamp from a persistent hash map", e)
}
catch (e: IOException) {
LOG.info("Cannot get a timestamp from a persistent hash map", e)
}
}
protected open fun getFileEditorManager(): FileEditorManagerEx? {
return getInstanceExIfCreated(project)
}
protected open fun getFileEditorManager(): FileEditorManagerEx? = getInstanceExIfCreated(project)
private fun registerViewed(file: VirtualFile) {
if (ApplicationManager.getApplication().isUnitTestMode() || !UISettings.getInstance().showInplaceComments) {
@@ -230,7 +201,7 @@ open class IdeDocumentHistoryImpl(
}
try {
recentFileTimestampMap.value!!.put(file.getPath(), System.currentTimeMillis())
recentFileTimestampMap.value.put(file.getPath(), System.currentTimeMillis())
}
catch (e: IOException) {
LOG.info("Cannot put a timestamp from a persistent hash map", e)
@@ -238,7 +209,9 @@ open class IdeDocumentHistoryImpl(
}
@Serializable
data class RecentlyChangedFilesState(val changedPaths: List<String> = emptyList<String>())
data class RecentlyChangedFilesState(
@JvmField val changedPaths: List<String> = emptyList<String>(),
)
override fun getState(): RecentlyChangedFilesState = state
@@ -261,20 +234,17 @@ open class IdeDocumentHistoryImpl(
}
fun onCommandStarted(commandGroupId: Any?) {
myCurrentCommandGroupId = commandGroupId
currentCommandGroupId = commandGroupId
commandStartPlace = getCurrentPlaceInfo()
currentCommandIsNavigation = false
currentCommandHasChanges = false
currentCommandHasMoves = false
reallyExcludeCurrentCommandFromNavigation = false
myChangedFilesInCurrentCommand.clear()
changedFilesInCurrentCommand.clear()
}
private fun getCurrentPlaceInfo(): PlaceInfo? {
val selectedEditorWithProvider = getSelectedEditor()
if (selectedEditorWithProvider == null) {
return null
}
val selectedEditorWithProvider = getSelectedEditor() ?: return null
return createPlaceInfo(selectedEditorWithProvider.fileEditor, selectedEditorWithProvider.provider)
}
@@ -294,19 +264,21 @@ open class IdeDocumentHistoryImpl(
}
fun onCommandFinished(project: Project?, commandGroupId: Any?) {
val lastGroupId = SoftReference.dereference<Any?>(this.lastGroupId)
if (!CommandMerger.canMergeGroup(commandGroupId, lastGroupId)) registeredBackPlaceInLastGroup = false
val lastGroupId = lastGroupId?.get()
if (!CommandMerger.canMergeGroup(commandGroupId, lastGroupId)) {
registeredBackPlaceInLastGroup = false
}
if (commandGroupId !== lastGroupId) {
this.lastGroupId = if (commandGroupId == null) null else WeakReference<Any?>(commandGroupId)
this.lastGroupId = commandGroupId?.let { WeakReference(it) }
}
val commandStartPlace = commandStartPlace
if (commandStartPlace != null && currentCommandIsNavigation && currentCommandHasMoves) {
if (!myBackInProgress) {
if (!backInProgress) {
if (!registeredBackPlaceInLastGroup) {
registeredBackPlaceInLastGroup = true
putLastOrMerge(next = commandStartPlace, limit = BACK_QUEUE_LIMIT, isChanged = false, groupId = commandGroupId)
registerViewed(commandStartPlace.getFile())
registerViewed(commandStartPlace.file)
}
if (!forwardInProgress) {
forwardPlaces.clear()
@@ -339,13 +311,13 @@ open class IdeDocumentHistoryImpl(
private fun setCurrentChangePlace(acceptPlaceFromFocus: Boolean) {
var placeInfo = getCurrentPlaceInfo()
if (placeInfo != null && !myChangedFilesInCurrentCommand.contains(placeInfo.getFile())) {
if (placeInfo != null && !changedFilesInCurrentCommand.contains(placeInfo.file)) {
placeInfo = null
}
if (placeInfo == null && acceptPlaceFromFocus) {
placeInfo = getPlaceInfoFromFocus(project)
}
if (placeInfo != null && !myChangedFilesInCurrentCommand.contains(placeInfo.getFile())) {
if (placeInfo != null && !changedFilesInCurrentCommand.contains(placeInfo.file)) {
placeInfo = null
}
if (placeInfo == null) {
@@ -354,7 +326,7 @@ open class IdeDocumentHistoryImpl(
val limit = UISettings.getInstance().recentFilesLimit + 1
synchronized(state) {
val path = placeInfo.getFile().getPath()
val path = placeInfo.file.getPath()
val changedPaths = state.changedPaths.toMutableList()
changedPaths.remove(path)
changedPaths.add(path)
@@ -364,7 +336,7 @@ open class IdeDocumentHistoryImpl(
state = RecentlyChangedFilesState(changedPaths)
}
putLastOrMerge(next = placeInfo, limit = CHANGE_QUEUE_LIMIT, isChanged = true, groupId = myCurrentCommandGroupId)
putLastOrMerge(next = placeInfo, limit = CHANGE_QUEUE_LIMIT, isChanged = true, groupId = currentCommandGroupId)
currentIndex = changePlaces.size
}
@@ -394,6 +366,7 @@ open class IdeDocumentHistoryImpl(
override fun back() {
removeInvalidFilesFromStacks()
@Suppress("UsePropertyAccessSyntax")
if (backPlaces.isEmpty()) {
return
}
@@ -406,24 +379,22 @@ open class IdeDocumentHistoryImpl(
forwardPlaces.add(current)
}
myBackInProgress = true
backInProgress = true
try {
executeCommand(Runnable { gotoPlaceInfo(info) }, "", null)
executeCommand(runnable = { gotoPlaceInfo(info) }, name = "", groupId = null)
}
finally {
myBackInProgress = false
backInProgress = false
}
}
override fun forward() {
removeInvalidFilesFromStacks()
val target = getTargetForwardInfo()
if (target == null) return
val target = getTargetForwardInfo() ?: return
forwardInProgress = true
try {
executeCommand(Runnable { gotoPlaceInfo(target) }, "", null)
executeCommand(runnable = { gotoPlaceInfo(target) }, name = "", groupId = null)
}
finally {
forwardInProgress = false
@@ -431,11 +402,15 @@ open class IdeDocumentHistoryImpl(
}
private fun getTargetForwardInfo(): PlaceInfo? {
if (forwardPlaces.isEmpty()) return null
@Suppress("UsePropertyAccessSyntax")
if (forwardPlaces.isEmpty()) {
return null
}
var target = forwardPlaces.removeLast()
val current = getCurrentPlaceInfo()
@Suppress("UsePropertyAccessSyntax")
while (!forwardPlaces.isEmpty()) {
if (current != null && isSame(current, target)) {
target = forwardPlaces.removeLast()
@@ -447,32 +422,36 @@ open class IdeDocumentHistoryImpl(
return target
}
override fun isBackAvailable(): Boolean {
return !backPlaces.isEmpty()
}
@Suppress("UsePropertyAccessSyntax")
override fun isBackAvailable(): Boolean = !backPlaces.isEmpty()
override fun isForwardAvailable(): Boolean {
return !forwardPlaces.isEmpty()
}
@Suppress("UsePropertyAccessSyntax")
override fun isForwardAvailable(): Boolean = !forwardPlaces.isEmpty()
override fun navigatePreviousChange() {
removeInvalidFilesFromStacks()
if (currentIndex == 0) return
if (currentIndex == 0) {
return
}
val currentPlace = getCurrentPlaceInfo()
val changePlaces = getChangePlaces()
for (i in currentIndex - 1 downTo 0) {
val info = changePlaces.get(i)
if (currentPlace == null || !isSame(currentPlace, info)) {
executeCommand(Runnable { gotoPlaceInfo(info, true) }, "", null)
executeCommand(runnable = { gotoPlaceInfo(info = info, requestFocus = true) }, name = "", groupId = null)
currentIndex = i
break
}
}
}
override fun navigateNextChange() {
final override fun navigateNextChange() {
removeInvalidFilesFromStacks()
if (currentIndex >= changePlaces.size) return
if (currentIndex >= changePlaces.size) {
return
}
val currentPlace = getCurrentPlaceInfo()
val changePlaces = getChangePlaces()
for (i in currentIndex until changePlaces.size) {
@@ -485,27 +464,22 @@ open class IdeDocumentHistoryImpl(
}
}
override fun getBackPlaces(): MutableList<PlaceInfo?> {
return java.util.List.copyOf<PlaceInfo?>(backPlaces)
final override fun getBackPlaces(): List<PlaceInfo> = java.util.List.copyOf(backPlaces)
final override fun getChangePlaces(): List<PlaceInfo> = java.util.List.copyOf(changePlaces)
final override fun removeBackPlace(placeInfo: PlaceInfo) {
removePlaceInfo(placeInfo = placeInfo, places = backPlaces, changed = false)
}
override fun getChangePlaces(): MutableList<PlaceInfo> {
return java.util.List.copyOf<PlaceInfo?>(changePlaces)
}
override fun removeBackPlace(placeInfo: PlaceInfo) {
removePlaceInfo(placeInfo, backPlaces, false)
}
override fun removeChangePlace(placeInfo: PlaceInfo) {
removePlaceInfo(placeInfo, changePlaces, true)
final override fun removeChangePlace(placeInfo: PlaceInfo) {
removePlaceInfo(placeInfo = placeInfo, places = changePlaces, changed = true)
}
private fun removePlaceInfo(placeInfo: PlaceInfo, places: MutableCollection<PlaceInfo>, changed: Boolean) {
val removed = places.remove(placeInfo)
if (removed) {
project.getMessageBus().syncPublisher<RecentPlacesListener>(RecentPlacesListener.Companion.TOPIC).recentPlaceRemoved(placeInfo,
changed)
project.getMessageBus().syncPublisher(RecentPlacesListener.Companion.TOPIC).recentPlaceRemoved(placeInfo, changed)
}
}
@@ -522,14 +496,12 @@ open class IdeDocumentHistoryImpl(
}
}
override fun isNavigateNextChangeAvailable(): Boolean {
return currentIndex < changePlaces.size
}
override fun isNavigateNextChangeAvailable(): Boolean = currentIndex < changePlaces.size
private fun removeInvalidFilesFrom(backPlaces: Deque<PlaceInfo>): Boolean {
return backPlaces.removeIf { info ->
val file = info.getFile()
(file is OptionallyIncluded && !(file as OptionallyIncluded).isIncludedInDocumentHistory(project)) || !file.isValid()
val file = info.file
(file is OptionallyIncluded && !file.isIncludedInDocumentHistory(project)) || !file.isValid()
}
}
@@ -538,9 +510,7 @@ open class IdeDocumentHistoryImpl(
}
interface SkipFromDocumentHistory : OptionallyIncluded {
override fun isIncludedInDocumentHistory(project: Project): Boolean {
return false
}
override fun isIncludedInDocumentHistory(project: Project): Boolean = false
}
override fun gotoPlaceInfo(info: PlaceInfo) {
@@ -548,21 +518,25 @@ open class IdeDocumentHistoryImpl(
}
override fun gotoPlaceInfo(info: PlaceInfo, requestFocus: Boolean) {
val editorManager = getFileEditorManager()
val openOptions = FileEditorOpenOptions()
.withUsePreviewTab(info.isPreviewTab())
.withRequestFocus(requestFocus)
.withReuseOpen()
.withOpenMode(info.getOpenMode())
val editorsWithProviders = editorManager!!.openFile(info.getFile(), info.getWindow(), openOptions)
val editorManager = getFileEditorManager()!!
val editorsWithProviders = editorManager.openFile(
file = info.file,
window = info.getWindow(),
options = FileEditorOpenOptions(
usePreviewTab = info.isPreviewTab,
requestFocus = requestFocus,
reuseOpen = true,
openMode = info.getOpenMode(),
),
)
editorManager.setSelectedEditor(info.getFile(), info.getEditorTypeId())
editorManager.setSelectedEditor(info.file, info.editorTypeId)
val list = editorsWithProviders.allEditorsWithProviders
for (item in list) {
val typeId = item.provider.getEditorTypeId()
if (typeId == info.getEditorTypeId()) {
item.fileEditor.setState(info.getNavigationState())
if (typeId == info.editorTypeId) {
item.fileEditor.setState(info.navigationState)
}
}
}
@@ -572,30 +546,36 @@ open class IdeDocumentHistoryImpl(
*/
protected open fun getSelectedEditor(): FileEditorWithProvider? {
val editorManager = getFileEditorManager()
val file = if (editorManager == null) null else editorManager.currentFile
return if (file == null) null else editorManager!!.getSelectedEditorWithProvider(file)
val file = editorManager?.currentFile ?: return null
return editorManager.getSelectedEditorWithProvider(file)
}
// used by Rider
protected open fun createPlaceInfo(fileEditor: FileEditor, fileProvider: FileEditorProvider): PlaceInfo? {
if (!fileEditor.isValid()) {
return null
}
val editorManager = getFileEditorManager()
val file = fileEditor.getFile()
LOG.assertTrue(file != null, fileEditor.javaClass.getName() + " getFile() returned null")
val file = requireNotNull(fileEditor.getFile()) {
"${fileEditor.javaClass.getName()} getFile() returned null"
}
if (file is SkipFromDocumentHistory && !(file as SkipFromDocumentHistory).isIncludedInDocumentHistory(project)) {
if (file is SkipFromDocumentHistory && !file.isIncludedInDocumentHistory(project)) {
return null
}
val state = fileEditor.getState(FileEditorStateLevel.NAVIGATION)
val window = if (editorManager == null) null else editorManager.currentWindow
val composite = if (window != null) window.getComposite(file) else null
return PlaceInfo(file, state, fileProvider.getEditorTypeId(), window, composite != null && composite.isPreview,
getCaretPosition(fileEditor), System.currentTimeMillis())
val window = editorManager?.currentWindow
val composite = window?.getComposite(file)
return PlaceInfo(
file = file,
navigationState = state,
editorTypeId = fileProvider.getEditorTypeId(),
window = window,
isPreviewTab = composite != null && composite.isPreview,
caretPosition = getCaretPosition(fileEditor),
timeStamp = System.currentTimeMillis(),
)
}
private fun getCaretPosition(fileEditor: FileEditor): RangeMarker? {
@@ -604,14 +584,15 @@ open class IdeDocumentHistoryImpl(
}
val editor = fileEditor.getEditor()
val offset = editor.getCaretModel().getOffset()
val offset = editor.getCaretModel().offset
return editor.getDocument().createRangeMarker(offset, offset)
}
private fun putLastOrMerge(next: PlaceInfo, limit: Int, isChanged: Boolean, groupId: Any?) {
val list: Deque<PlaceInfo> = if (isChanged) changePlaces else backPlaces
val list = if (isChanged) changePlaces else backPlaces
val messageBus = project.getMessageBus()
val listener = messageBus.syncPublisher<RecentPlacesListener>(RecentPlacesListener.Companion.TOPIC)
val listener = messageBus.syncPublisher(RecentPlacesListener.TOPIC)
@Suppress("UsePropertyAccessSyntax")
if (!list.isEmpty()) {
val prev = list.getLast()
if (isSame(prev, next)) {
@@ -629,28 +610,24 @@ open class IdeDocumentHistoryImpl(
}
private fun getFileDocumentManager(): FileDocumentManager {
if (myFileDocumentManager == null) {
myFileDocumentManager = FileDocumentManager.getInstance()
var fileDocumentManager = fileDocumentManager
if (fileDocumentManager == null) {
fileDocumentManager = FileDocumentManager.getInstance()
this.fileDocumentManager = fileDocumentManager
}
return myFileDocumentManager!!
return fileDocumentManager
}
class PlaceInfo(
file: VirtualFile,
navigationState: FileEditorState,
editorTypeId: String,
val file: VirtualFile,
val navigationState: FileEditorState,
val editorTypeId: String,
window: EditorWindow?,
isPreviewTab: Boolean,
caretPosition: RangeMarker?,
stamp: Long
val isPreviewTab: Boolean,
val caretPosition: RangeMarker?,
val timeStamp: Long
) {
private val myFile = file
private val myNavigationState = navigationState
private val myEditorTypeId = editorTypeId
private val myWindow: Reference<EditorWindow?> = WeakReference<EditorWindow?>(window)
private val myIsPreviewTab: Boolean = isPreviewTab
private val myCaretPosition = caretPosition
private val myTimeStamp = stamp
private val windowRef = WeakReference(window)
constructor(
file: VirtualFile,
@@ -658,59 +635,34 @@ open class IdeDocumentHistoryImpl(
editorTypeId: String,
window: EditorWindow?,
caretPosition: RangeMarker?
) : this(file, navigationState, editorTypeId, window, false, caretPosition, -1)
) : this(
file = file,
navigationState = navigationState,
editorTypeId = editorTypeId,
window = window,
isPreviewTab = false,
caretPosition = caretPosition,
timeStamp = -1,
)
fun getWindow(): EditorWindow? {
return myWindow.get()
}
fun getWindow(): EditorWindow? = windowRef.get()
fun getNavigationState(): FileEditorState {
return myNavigationState
}
fun getFile(): VirtualFile {
return myFile
}
fun getEditorTypeId(): String {
return myEditorTypeId
}
override fun toString(): String {
return getFile().getName() + " " + getNavigationState()
}
fun getCaretPosition(): RangeMarker? {
return myCaretPosition
}
fun getTimeStamp(): Long {
return myTimeStamp
}
fun isPreviewTab(): Boolean {
return myIsPreviewTab
}
override fun toString(): String = file.getName() + " " + navigationState
@ApiStatus.Internal
fun getOpenMode(): FileEditorManagerImpl.OpenMode? {
if (myNavigationState is FileEditorStateWithPreferredOpenMode) {
return myNavigationState.openMode
}
return null
return (navigationState as? FileEditorStateWithPreferredOpenMode)?.let { return it.openMode }
}
}
override fun dispose() {
lastGroupId = null
val map = recentFileTimestampMap.valueIfInitialized
if (map != null) {
try {
map.close()
}
catch (e: IOException) {
LOG.info("Cannot close persistent viewed files timestamps hash map", e)
}
val map = recentFileTimestampMap.valueIfInitialized ?: return
try {
map.close()
}
catch (e: IOException) {
LOG.info("Cannot close persistent viewed files timestamps hash map", e)
}
}
@@ -719,12 +671,11 @@ open class IdeDocumentHistoryImpl(
}
override fun isSame(first: PlaceInfo, second: PlaceInfo): Boolean {
if (first.getFile() == second.getFile()) {
val firstState = first.getNavigationState()
val secondState = second.getNavigationState()
if (first.file == second.file) {
val firstState = first.navigationState
val secondState = second.navigationState
return firstState == secondState || firstState.canBeMergedWith(secondState, FileEditorStateLevel.NAVIGATION)
}
return false
}
@@ -766,7 +717,26 @@ open class IdeDocumentHistoryImpl(
* @param groupId groupId of the command that caused the change place addition
*/
fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean, groupId: Any?) {
@Suppress("DEPRECATION")
recentPlaceAdded(changePlace, isChanged)
}
}
}
private fun initRecentFilesTimestampMap(project: Project): PersistentHashMap<String, Long> {
val file = project.getProjectCachePath("recentFilesTimeStamps.dat")
try {
return IOUtil.openCleanOrResetBroken({
PersistentHashMap(file,
EnumeratorStringDescriptor.INSTANCE,
EnumeratorLongDescriptor,
256,
0,
StorageLockContext())
}, file)
}
catch (e: IOException) {
LOG.error("Cannot create PersistentHashMap in $file", e)
throw RuntimeException(e)
}
}

View File

@@ -1,11 +1,11 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.fileEditor
import com.intellij.codeInsight.navigation.activateFileWithPsiElement
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory
import com.intellij.openapi.fileEditor.impl.FileEditorOpenOptions
import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl
import com.intellij.openapi.util.EmptyRunnable
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
@@ -16,7 +16,7 @@ import org.junit.Assert
class NewDocumentHistoryTest : HeavyFileEditorManagerTestCase() {
override fun tearDown() {
try {
IdeDocumentHistoryImpl.getInstance(project).clearHistory()
IdeDocumentHistory.getInstance(project).clearHistory()
}
catch (e: Throwable) {
addSuppressedException(e)
@@ -40,7 +40,7 @@ class NewDocumentHistoryTest : HeavyFileEditorManagerTestCase() {
executeSomeCoroutineTasksAndDispatchAllInvocationEvents(project)
assertThat(manager.getSelectedEditor(file)!!.name).isEqualTo(FileEditorManagerTest.MyFileEditorProvider.DEFAULT_FILE_EDITOR_NAME)
manager.closeAllFiles()
IdeDocumentHistoryImpl.getInstance(project).back()
IdeDocumentHistory.getInstance(project).back()
assertThat(manager.getSelectedEditor(file)?.name).isEqualTo(FileEditorManagerTest.MyFileEditorProvider.DEFAULT_FILE_EDITOR_NAME)
}
@@ -68,7 +68,7 @@ class NewDocumentHistoryTest : HeavyFileEditorManagerTestCase() {
CommandProcessor.getInstance().executeCommand(project, {
manager.openFile(file = file3!!, focusEditor = true)
}, null, group)
IdeDocumentHistoryImpl.getInstance(project).back()
IdeDocumentHistory.getInstance(project).back()
val selectedFiles = manager.selectedFiles
Assert.assertArrayEquals(arrayOf<VirtualFile?>(file2), selectedFiles)
}

View File

@@ -1,7 +1,8 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.fileEditor.impl;
import com.intellij.mock.Mock;
import com.intellij.openapi.components.ComponentManagerEx;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider;
@@ -47,7 +48,8 @@ public class IdeDocumentHistoryTest extends HeavyPlatformTestCase {
};
EditorManager editorManager = new EditorManager();
myHistory = new IdeDocumentHistoryImpl(getProject()) {
Project project = getProject();
myHistory = new IdeDocumentHistoryImpl(project, ((ComponentManagerEx)project).getCoroutineScope()) {
@Override
protected FileEditorManagerEx getFileEditorManager() {
return editorManager;
@@ -59,7 +61,7 @@ public class IdeDocumentHistoryTest extends HeavyPlatformTestCase {
}
@Override
protected void executeCommand(Runnable runnable, String name, Object groupId) {
protected void executeCommand(@NotNull Runnable runnable, String name, Object groupId) {
myHistory.onCommandStarted(groupId);
runnable.run();
myHistory.onSelectionChanged();

View File

@@ -23,9 +23,11 @@ import java.util.function.BooleanSupplier
class RecentPlacesFeatures : ElementFeatureProvider {
override fun getName(): String = "recent_places"
override fun calculateFeatures(element: LookupElement,
location: CompletionLocation,
contextFeatures: ContextFeatures): Map<String, MLFeatureValue> {
override fun calculateFeatures(
element: LookupElement,
location: CompletionLocation,
contextFeatures: ContextFeatures,
): Map<String, MLFeatureValue> {
val storage = location.project.service<RecentPlacesStorage>()
val inRecentPlaces = storage.contains(element.lookupString)
val inChildrenRecentPlaces = storage.childrenContains(element.lookupString)
@@ -44,7 +46,10 @@ class RecentPlacesFeatures : ElementFeatureProvider {
private val recentPlacesStorage = project.service<RecentPlacesStorage>()
override fun recentPlaceAdded(changePlace: IdeDocumentHistoryImpl.PlaceInfo, isChanged: Boolean) {
if (ApplicationManager.getApplication().isUnitTestMode || !changePlace.file.isValid || changePlace.file.isDirectory) return
if (ApplicationManager.getApplication().isUnitTestMode || !changePlace.file.isValid || changePlace.file.isDirectory) {
return
}
val offset = changePlace.caretPosition?.startOffset ?: return
@Suppress("IncorrectParentDisposable")
@@ -69,23 +74,27 @@ class RecentPlacesFeatures : ElementFeatureProvider {
}
.coalesceBy(this, changePlace.file)
.expireWith(project)
.expireWhen(BooleanSupplier { changePlace.window == null || changePlace.window.isDisposed })
.expireWhen(BooleanSupplier {
val window = changePlace.getWindow()
window == null || window.isDisposed
})
.submit(AppExecutorUtil.getAppExecutorService())
}
override fun recentPlaceRemoved(changePlace: IdeDocumentHistoryImpl.PlaceInfo, isChanged: Boolean) = Unit
private fun PsiElement.getChildrenNames(): List<String> =
this.children.filterIsInstance<PsiNamedElement>().mapNotNull { it.name }
private fun PsiElement.getChildrenNames(): List<String> {
return this.children.filterIsInstance<PsiNamedElement>().mapNotNull { it.name }
}
private fun FileViewProvider.tryFindElementAt(offset: Int): PsiElement? =
try {
if (virtualFile.isValid && getPsi(baseLanguage)?.isValid == true)
findElementAt(offset)
else null
} catch (t: Throwable) {
private fun FileViewProvider.tryFindElementAt(offset: Int): PsiElement? {
return try {
if (virtualFile.isValid && getPsi(baseLanguage)?.isValid == true) findElementAt(offset) else null
}
catch (_: Throwable) {
null
}
}
private fun findDeclaration(element: PsiElement): PsiElement? {
var curElement = element