diff --git a/platform/diff-impl/resources/api-dump.txt b/platform/diff-impl/resources/api-dump.txt index 9432855b67d8..6184559d68cd 100644 --- a/platform/diff-impl/resources/api-dump.txt +++ b/platform/diff-impl/resources/api-dump.txt @@ -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 diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewInplaceCommentProducerImpl.kt b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewInplaceCommentProducerImpl.kt index b05f5242a2e4..504ea2ed21ec 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewInplaceCommentProducerImpl.kt +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewInplaceCommentProducerImpl.kt @@ -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 AbstractTreeNode.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 cantBeFile(node: AbstractTreeNode): 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 AbstractTreeNode.isDirectory(): Boolean { - val value = this.value +private fun isDirectory(node: AbstractTreeNode): 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 AbstractTreeNode.isFile(): Boolean = getVirtualFileForNodeOrItsPSI()?.isFile == true +private fun isFile(node: AbstractTreeNode): 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) } } diff --git a/platform/platform-impl/api-dump-unreviewed.txt b/platform/platform-impl/api-dump-unreviewed.txt index 6696959be50d..abbf24b5c475 100644 --- a/platform/platform-impl/api-dump-unreviewed.txt +++ b/platform/platform-impl/api-dump-unreviewed.txt @@ -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 -- (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 -- (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 -- (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 - ():V diff --git a/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsAction.java index 2e28a10e5d71..4477c07f8588 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsAction.java @@ -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> supplier, @Nullable Consumer> 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 list = new JBList<>(JBList.createDefaultListModel(model.getPlaces(showChanged))); final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(list, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, diff --git a/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsDataModel.kt b/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsDataModel.kt index 9e0e9478471e..082bb2d2a574 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsDataModel.kt +++ b/platform/platform-impl/src/com/intellij/ide/actions/RecentLocationsDataModel.kt @@ -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>?, - private val placesRemover: Consumer>?) { - +internal class RecentLocationsDataModel( + private val project: Project, + private val placesSupplier: Function>?, + private val placesRemover: ((List) -> Unit)?, +) { private val navigationPlaces: SynchronizedClearableLazy> = calculateItems(project, false) private val changedPlaces: SynchronizedClearableLazy> = 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) { - 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 ) { - 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 diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/IdeDocumentHistory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/IdeDocumentHistory.java index 39eb77263135..7aca095811d6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/IdeDocumentHistory.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/IdeDocumentHistory.java @@ -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 getChangedFiles(); + @ApiStatus.Internal public abstract List getChangePlaces(); + @ApiStatus.Internal public abstract List 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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.kt b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.kt index dafa414d234b..c46d9fa0f5ce 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.kt @@ -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 { - private var myFileDocumentManager: FileDocumentManager? = null + private var fileDocumentManager: FileDocumentManager? = null - private val backPlaces: Deque = ArrayDeque() - private val forwardPlaces: Deque = ArrayDeque() - private var myBackInProgress = false + private val backPlaces = ArrayDeque() + private val forwardPlaces = ArrayDeque() + private var backInProgress = false private var forwardInProgress = false - private var myCurrentCommandGroupId: Any? = null - private var lastGroupId: Reference? = 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? = null private var registeredBackPlaceInLastGroup = false // change's navigation - private val changePlaces: Deque = ArrayDeque() + private val changePlaces = ArrayDeque() private var currentIndex = 0 private var commandStartPlace: PlaceInfo? = null private var currentCommandIsNavigation = false private var currentCommandHasChanges = false - private val myChangedFilesInCurrentCommand = HashSet() + private val changedFilesInCurrentCommand = HashSet() private var currentCommandHasMoves = false private var reallyExcludeCurrentCommandFromNavigation = false - private val recentFileTimestampMap: SynchronizedClearableLazy?> + private val recentFileTimestampMap: SynchronizedClearableLazy> + @Volatile private var state = RecentlyChangedFilesState() init { - val busConnection = project.getMessageBus().connect(this) - busConnection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener { - override fun after(events: MutableList) { + val connection = project.getMessageBus().connect(coroutineScope) + connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener { + override fun after(events: List) { for (event in events) { if (event is VFileDeleteEvent) { removeInvalidFilesFromStacks() @@ -113,42 +120,41 @@ open class IdeDocumentHistoryImpl( } } }) - busConnection.subscribe(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?> { 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 { 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 { - val file = project.getProjectCachePath("recentFilesTimeStamps.dat") - try { - return IOUtil.openCleanOrResetBroken>(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 { - return PersistentHashMap(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 = emptyList()) + data class RecentlyChangedFilesState( + @JvmField val changedPaths: List = emptyList(), + ) 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(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(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 { - return java.util.List.copyOf(backPlaces) + final override fun getBackPlaces(): List = java.util.List.copyOf(backPlaces) + + final override fun getChangePlaces(): List = java.util.List.copyOf(changePlaces) + + final override fun removeBackPlace(placeInfo: PlaceInfo) { + removePlaceInfo(placeInfo = placeInfo, places = backPlaces, changed = false) } - override fun getChangePlaces(): MutableList { - return java.util.List.copyOf(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, changed: Boolean) { val removed = places.remove(placeInfo) if (removed) { - project.getMessageBus().syncPublisher(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): 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 = if (isChanged) changePlaces else backPlaces + val list = if (isChanged) changePlaces else backPlaces val messageBus = project.getMessageBus() - val listener = messageBus.syncPublisher(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 = WeakReference(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 { + 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) + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/NewDocumentHistoryTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/NewDocumentHistoryTest.kt index c1fc4006201b..14ea11fb5207 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/NewDocumentHistoryTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/NewDocumentHistoryTest.kt @@ -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(file2), selectedFiles) } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryTest.java index c90130372416..932de07994d0 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryTest.java @@ -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(); diff --git a/plugins/completion-ml-ranking/src/com/intellij/completion/ml/common/RecentPlacesFeatures.kt b/plugins/completion-ml-ranking/src/com/intellij/completion/ml/common/RecentPlacesFeatures.kt index 82f875f43472..9bca793555f7 100644 --- a/plugins/completion-ml-ranking/src/com/intellij/completion/ml/common/RecentPlacesFeatures.kt +++ b/plugins/completion-ml-ranking/src/com/intellij/completion/ml/common/RecentPlacesFeatures.kt @@ -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 { + override fun calculateFeatures( + element: LookupElement, + location: CompletionLocation, + contextFeatures: ContextFeatures, + ): Map { val storage = location.project.service() val inRecentPlaces = storage.contains(element.lookupString) val inChildrenRecentPlaces = storage.childrenContains(element.lookupString) @@ -44,7 +46,10 @@ class RecentPlacesFeatures : ElementFeatureProvider { private val recentPlacesStorage = project.service() 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 = - this.children.filterIsInstance().mapNotNull { it.name } + private fun PsiElement.getChildrenNames(): List { + return this.children.filterIsInstance().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