mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[ByteCodeViewer] IDEA-375222 Update tab name when tabs are closed
Current problems: - does not respect the application-wide "Show directory for non-unique file names" setting GitOrigin-RevId: 8b68558206017503fad07a55f0e3505ba3f73da9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
959522053c
commit
0b01dc7106
@@ -0,0 +1,91 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.byteCodeViewer
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.wm.ToolWindow
|
||||
import com.intellij.ui.content.Content
|
||||
import com.intellij.ui.content.ContentManagerEvent
|
||||
import com.intellij.ui.content.ContentManagerListener
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Ensures that the [ContentManagerListener] is registered only once
|
||||
* and properly unregistered when the project is disposed.
|
||||
*/
|
||||
@Service(Service.Level.PROJECT)
|
||||
class BytecodeToolWindowService(private val project: Project) {
|
||||
private val registeredListeners = ConcurrentHashMap<ToolWindow, ContentManagerListener>()
|
||||
|
||||
/**
|
||||
* Registers a ContentManagerListener for the BytecodeViewer tool window if not already registered.
|
||||
* The listener is responsible for updating tab titles when tabs are added or removed.
|
||||
*
|
||||
* If the listener for a specific instance of [ToolWindow] is already registered, this is a no-op.
|
||||
*/
|
||||
fun ensureContentManagerListenerRegistered(toolWindow: ToolWindow) {
|
||||
val contentManager = toolWindow.contentManager
|
||||
|
||||
// Check if the listener is already registered
|
||||
if (registeredListeners.containsKey(toolWindow)) {
|
||||
return
|
||||
}
|
||||
|
||||
val listener = object : ContentManagerListener {
|
||||
override fun contentAdded(event: ContentManagerEvent) {
|
||||
deduplicateTabNames(toolWindow)
|
||||
}
|
||||
|
||||
override fun contentRemoved(event: ContentManagerEvent) {
|
||||
deduplicateTabNames(toolWindow)
|
||||
}
|
||||
}
|
||||
contentManager.addContentManagerListener(listener)
|
||||
registeredListeners[toolWindow] = listener
|
||||
|
||||
Disposer.register(toolWindow.disposable) {
|
||||
contentManager.removeContentManagerListener(listener)
|
||||
registeredListeners.remove(toolWindow)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates tab titles to avoid duplicate names by adding path information when necessary.
|
||||
*/
|
||||
fun deduplicateTabNames(toolWindow: ToolWindow) {
|
||||
val titlesToContents = mutableMapOf<String, MutableList<Content>>()
|
||||
for (content in toolWindow.contentManager.contents) {
|
||||
val classFileName = content.getUserData(JAVA_CLASS_FILE)?.name
|
||||
?: throw IllegalStateException("Content entry has no JAVA_CLASS_FILE or it is null. Entry: $content")
|
||||
titlesToContents.getOrPut(classFileName) { mutableListOf() }.add(content)
|
||||
}
|
||||
for ((classFileName, contents) in titlesToContents) {
|
||||
if (contents.size == 1) {
|
||||
contents[0].displayName = classFileName
|
||||
}
|
||||
else if (contents.size > 1) {
|
||||
val paths = contents.map {
|
||||
it.getUserData(JAVA_CLASS_FILE) ?: throw IllegalStateException("No class file path for content entry $it")
|
||||
}
|
||||
|
||||
val commonAncestor = VfsUtil.getCommonAncestor(paths) ?: continue
|
||||
|
||||
for (i in contents.indices) {
|
||||
val content = contents[i]
|
||||
content.displayName = VfsUtil.getRelativePath(
|
||||
content.getUserData(JAVA_CLASS_FILE) ?: throw IllegalStateException("No class file path for content entry $content"),
|
||||
commonAncestor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): BytecodeToolWindowService {
|
||||
return project.getService(BytecodeToolWindowService::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,12 @@ import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.wm.ToolWindow
|
||||
import com.intellij.openapi.wm.ToolWindowAnchor
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
import com.intellij.ui.content.Content
|
||||
import com.intellij.ui.content.ContentFactory
|
||||
|
||||
private val JAVA_SOURCE_FILE = Key.create<VirtualFile>("JAVA_SOURCE_FILE")
|
||||
private val JAVA_CLASS_FILE = Key.create<VirtualFile>("JAVA_CLASS_FILE")
|
||||
internal val JAVA_CLASS_FILE = Key.create<VirtualFile>("JAVA_CLASS_FILE")
|
||||
|
||||
internal class ShowBytecodeAction : AnAction() {
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
@@ -35,6 +31,10 @@ internal class ShowBytecodeAction : AnAction() {
|
||||
hideOnEmptyContent = true
|
||||
canCloseContent = true
|
||||
}
|
||||
val service = BytecodeToolWindowService.getInstance(project)
|
||||
|
||||
service.ensureContentManagerListenerRegistered(toolWindow)
|
||||
|
||||
val editor = event.getData(CommonDataKeys.EDITOR) ?: return
|
||||
val psiFile = event.getData(CommonDataKeys.PSI_FILE) ?: return
|
||||
val psiElement = psiFile.findElementAt(editor.caretModel.offset) ?: return
|
||||
@@ -77,38 +77,13 @@ internal class ShowBytecodeAction : AnAction() {
|
||||
|
||||
|
||||
toolWindow.contentManager.addContent(content)
|
||||
deduplicateTabNames(toolWindow)
|
||||
service.deduplicateTabNames(toolWindow)
|
||||
content.setDisposer(panel)
|
||||
toolWindow.contentManager.setSelectedContent(content)
|
||||
toolWindow.setAdditionalGearActions(createActionGroup())
|
||||
toolWindow.activate(null)
|
||||
}
|
||||
|
||||
private fun deduplicateTabNames(toolWindow: ToolWindow) {
|
||||
val titlesToContents = mutableMapOf<String, MutableList<Content>>()
|
||||
for (contentEntry in toolWindow.contentManager.contents) {
|
||||
val tabName = contentEntry.getUserData(JAVA_CLASS_FILE)?.name ?: throw IllegalStateException("No title for content entry $contentEntry")
|
||||
titlesToContents.getOrPut(tabName) { mutableListOf() }.add(contentEntry)
|
||||
}
|
||||
for (contents in titlesToContents.values) {
|
||||
if (contents.size > 1) {
|
||||
val paths = contents.map {
|
||||
it.getUserData(JAVA_CLASS_FILE) ?: throw IllegalStateException("No class file path for content entry $it")
|
||||
}
|
||||
|
||||
val commonAncestor = VfsUtil.getCommonAncestor(paths) ?: continue
|
||||
|
||||
for (i in contents.indices) {
|
||||
val content = contents[i]
|
||||
content.displayName = VfsUtil.getRelativePath(
|
||||
content.getUserData(JAVA_CLASS_FILE) ?: throw IllegalStateException("No class file path for content entry $content"),
|
||||
commonAncestor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createActionGroup(): ActionGroup {
|
||||
val action = object : ToggleAction(BytecodeViewerBundle.messagePointer("action.show.debug.action.name")) {
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
Reference in New Issue
Block a user