From e0da3861d1b0dfd8d61217e870b822b28de18fda Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 3 Jan 2025 14:00:36 +0100 Subject: [PATCH] bookmarks: do not handle document changes while external reloading IJPL-62074 GitOrigin-RevId: 4b215d8fdc1bc60948006879c66a2f4f4cf2ee9e --- platform/bookmarks/api-dump.txt | 4 -- .../providers/LineBookmarkProvider.kt | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/platform/bookmarks/api-dump.txt b/platform/bookmarks/api-dump.txt index a6597356da5a..4a35386f46cf 100644 --- a/platform/bookmarks/api-dump.txt +++ b/platform/bookmarks/api-dump.txt @@ -67,11 +67,8 @@ f:com.intellij.ide.bookmark.providers.LineBookmarkImpl - navigate(Z):V f:com.intellij.ide.bookmark.providers.LineBookmarkProvider - com.intellij.ide.bookmark.BookmarkProvider -- com.intellij.openapi.editor.event.BulkAwareDocumentListener$Simple -- com.intellij.openapi.vfs.AsyncFileListener - sf:Companion:com.intellij.ide.bookmark.providers.LineBookmarkProvider$Companion - (com.intellij.openapi.project.Project,kotlinx.coroutines.CoroutineScope):V -- afterDocumentChange(com.intellij.openapi.editor.Document):V - compare(com.intellij.ide.bookmark.Bookmark,com.intellij.ide.bookmark.Bookmark):I - f:createBookmark(com.intellij.openapi.editor.Editor,java.lang.Integer):com.intellij.ide.bookmark.FileBookmark - f:createBookmark(com.intellij.openapi.vfs.VirtualFile,I):com.intellij.ide.bookmark.FileBookmark @@ -82,7 +79,6 @@ f:com.intellij.ide.bookmark.providers.LineBookmarkProvider - sf:find(com.intellij.openapi.project.Project):com.intellij.ide.bookmark.providers.LineBookmarkProvider - getProject():com.intellij.openapi.project.Project - getWeight():I -- prepareChange(java.util.List):com.intellij.openapi.vfs.AsyncFileListener$ChangeApplier - prepareGroup(java.util.List):java.util.List f:com.intellij.ide.bookmark.providers.LineBookmarkProvider$Companion - f:find(com.intellij.openapi.project.Project):com.intellij.ide.bookmark.providers.LineBookmarkProvider diff --git a/platform/bookmarks/src/com/intellij/ide/bookmark/providers/LineBookmarkProvider.kt b/platform/bookmarks/src/com/intellij/ide/bookmark/providers/LineBookmarkProvider.kt index 11def4207275..b9948867b9fd 100644 --- a/platform/bookmarks/src/com/intellij/ide/bookmark/providers/LineBookmarkProvider.kt +++ b/platform/bookmarks/src/com/intellij/ide/bookmark/providers/LineBookmarkProvider.kt @@ -1,6 +1,7 @@ // 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.bookmark.providers +import com.intellij.concurrency.ConcurrentCollectionFactory import com.intellij.ide.bookmark.* import com.intellij.ide.bookmark.ui.tree.FileNode import com.intellij.ide.bookmark.ui.tree.LineNode @@ -12,12 +13,13 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.editor.event.BulkAwareDocumentListener.Simple import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.fileEditor.FileDocumentManagerListener import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.AsyncFileListener import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.VirtualFileManager.getInstance import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent @@ -33,7 +35,7 @@ import kotlinx.coroutines.CoroutineScope import javax.swing.tree.TreePath @Suppress("ExtensionClassShouldBeFinalAndNonPublic") -class LineBookmarkProvider(private val project: Project, coroutineScope: CoroutineScope) : BookmarkProvider, Simple, AsyncFileListener { +class LineBookmarkProvider(private val project: Project, coroutineScope: CoroutineScope) : BookmarkProvider { override fun getWeight(): Int = Int.MIN_VALUE override fun getProject(): Project = project @@ -116,7 +118,7 @@ class LineBookmarkProvider(private val project: Project, coroutineScope: Corouti } private fun createBookmark(url: String, line: Int = -1) = createValidBookmark(url, line) ?: createInvalidBookmark(url, line) - private fun createValidBookmark(url: String, line: Int = -1) = VFM.findFileByUrl(url)?.let { createBookmark(it, line) } + private fun createValidBookmark(url: String, line: Int = -1) = getInstance().findFileByUrl(url)?.let { createBookmark(it, line) } private fun createInvalidBookmark(url: String, line: Int = -1) = InvalidBookmark(this, url, line) private fun createBookmark(element: PsiElement): FileBookmark? { @@ -141,7 +143,8 @@ class LineBookmarkProvider(private val project: Project, coroutineScope: Corouti private val TreePath.asVirtualFile get() = TreeUtil.getLastUserObject(ProjectViewNode::class.java, this)?.virtualFile - override fun afterDocumentChange(document: Document) { + private fun afterDocumentChange(document: Document) { + if (reloadingDocs.contains(document)) return val file = FileDocumentManager.getInstance().getFile(document) ?: return if (file is LightVirtualFile) return val manager = BookmarksManager.getInstance(project) ?: return @@ -171,13 +174,14 @@ class LineBookmarkProvider(private val project: Project, coroutineScope: Corouti manager.update(bookmarks) } - override fun prepareChange(events: List): AsyncFileListener.ChangeApplier? { + private fun prepareChange(events: List): AsyncFileListener.ChangeApplier? { val update = events.any { it is VFileCreateEvent || it is VFileDeleteEvent } if (update) validateAlarm.cancelAndRequest() return null } private val validateAlarm = SingleAlarm.singleAlarm(task = ::validateAndUpdate, delay = 100, coroutineScope = coroutineScope) + private val reloadingDocs = ConcurrentCollectionFactory.createConcurrentSet() private fun validateAndUpdate() { val manager = BookmarksManager.getInstance(project) ?: return @@ -193,15 +197,30 @@ class LineBookmarkProvider(private val project: Project, coroutineScope: Corouti else -> null } - private val VFM - get() = VirtualFileManager.getInstance() private fun isNodeVisible(node: AbstractTreeNode<*>) = (node.value as? InvalidBookmark)?.run { line < 0 } ?: true init { if (!project.isDefault) { val multicaster = EditorFactory.getInstance().eventMulticaster - multicaster.addDocumentListener(this, project) - VFM.addAsyncFileListener(this, project) + multicaster.addDocumentListener(object : Simple { + override fun afterDocumentChange(document: Document) { + this@LineBookmarkProvider.afterDocumentChange(document) + } + }, project) + getInstance().addAsyncFileListener(object : AsyncFileListener { + override fun prepareChange(events: List): AsyncFileListener.ChangeApplier? { + return this@LineBookmarkProvider.prepareChange(events) + } + }, project) + + project.messageBus.connect().subscribe(FileDocumentManagerListener.TOPIC, object : FileDocumentManagerListener { + override fun beforeFileContentReload(file: VirtualFile, document: Document) { + reloadingDocs.add(document) + } + override fun fileContentReloaded(file: VirtualFile, document: Document) { + reloadingDocs.remove(document) + } + }) } }