From eb3aaca34a5d4551f736cf811d08388c44bfe5b8 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 27 May 2015 15:27:43 +0300 Subject: [PATCH] IDEA-127197 Remote debug: easy way to change library used to display sources --- .../debugger/engine/JavaDebugProcess.java | 15 ++ .../debugger/impl/DebuggerUtilsEx.java | 11 ++ ...AlternativeSourceNotificationProvider.java | 180 ++++++++++++++++++ .../src/messages/DebuggerBundle.properties | 3 +- resources/src/idea/RichPlatformPlugin.xml | 1 + 5 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java index 7dd68b3cc546..30f778c159c6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java @@ -23,6 +23,7 @@ import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.impl.*; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.settings.DebuggerSettings; +import com.intellij.debugger.ui.AlternativeSourceNotificationProvider; import com.intellij.debugger.ui.DebuggerContentInfo; import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.impl.ThreadsPanel; @@ -43,6 +44,8 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.EditorNotifications; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; @@ -154,17 +157,29 @@ public class JavaDebugProcess extends XDebugProcess { @Override public void sessionPaused() { saveNodeHistory(); + showAlternativeNotification(session.getCurrentStackFrame()); } @Override public void stackFrameChanged() { XStackFrame frame = session.getCurrentStackFrame(); if (frame instanceof JavaStackFrame) { + showAlternativeNotification(frame); StackFrameProxyImpl frameProxy = ((JavaStackFrame)frame).getStackFrameProxy(); DebuggerContextUtil.setStackFrame(javaSession.getContextManager(), frameProxy); saveNodeHistory(frameProxy); } } + + private void showAlternativeNotification(XStackFrame frame) { + XSourcePosition position = frame.getSourcePosition(); + if (position != null) { + VirtualFile file = position.getFile(); + if (!AlternativeSourceNotificationProvider.fileProcessed(file)) { + EditorNotifications.getInstance(session.getProject()).updateNotifications(file); + } + } + } }); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 02883527f75b..c645439010ee 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -700,6 +700,13 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { return new JavaXSourcePosition(position, file); } + private static final Key ALTERNATIVE_SOURCE_KEY = new Key("DEBUGGER_ALTERNATIVE_SOURCE"); + + public static void setAlternativeSource(VirtualFile source, VirtualFile dest) { + ALTERNATIVE_SOURCE_KEY.set(source, dest); + ALTERNATIVE_SOURCE_KEY.set(dest, null); + } + private static class JavaXSourcePosition implements XSourcePosition, ExecutionPointHighlighter.HighlighterProvider { private final SourcePosition mySourcePosition; @NotNull private final VirtualFile myFile; @@ -722,6 +729,10 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { @NotNull @Override public VirtualFile getFile() { + VirtualFile file = ALTERNATIVE_SOURCE_KEY.get(myFile); + if (file != null) { + return file; + } return myFile; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java b/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java new file mode 100644 index 000000000000..345b43bb233b --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java @@ -0,0 +1,180 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.ui; + +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.impl.DebuggerUtilsEx; +import com.intellij.ide.highlighter.JavaClassFileType; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.EditorNotificationPanel; +import com.intellij.ui.EditorNotifications; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +/** + * @author egor + */ +public class AlternativeSourceNotificationProvider extends EditorNotifications.Provider { + private static final Key KEY = Key.create("AlternativeSource"); + private static final Key FILE_PROCESSED_KEY = Key.create("AlternativeSourceCheckDone"); + private final Project myProject; + + public AlternativeSourceNotificationProvider(Project project) { + myProject = project; + } + + @NotNull + @Override + public Key getKey() { + return KEY; + } + + @Nullable + @Override + public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) { + if (XDebuggerManager.getInstance(myProject).getCurrentSession() == null) { + FILE_PROCESSED_KEY.set(file, null); + return null; + } + + if (file.getFileType() == JavaClassFileType.INSTANCE) return null; + + final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file); + if (psiFile == null) return null; + + if (!(psiFile instanceof PsiJavaFile)) return null; + + PsiClass[] classes = ((PsiJavaFile)psiFile).getClasses(); + if (classes.length == 0) return null; + + PsiClass baseClass = classes[0]; + String name = baseClass.getQualifiedName(); + + if (name == null) return null; + + if (DumbService.getInstance(myProject).isDumb()) return null; + + PsiClass[] alternatives = JavaPsiFacade.getInstance(myProject).findClasses(name, GlobalSearchScope.allScope(myProject)); + + FILE_PROCESSED_KEY.set(file, true); + + if (alternatives.length > 1) { + ArrayList alts = ContainerUtil.newArrayList(alternatives); + for (PsiClass cls : alts) { + if (cls.equals(baseClass) || cls.getNavigationElement().equals(baseClass)) { + alts.remove(cls); + break; + } + } + alts.add(0, baseClass); + + ComboBoxClassElement[] elems = ContainerUtil.map2Array(alts, + ComboBoxClassElement.class, + new Function() { + @Override + public ComboBoxClassElement fun(PsiClass psiClass) { + return new ComboBoxClassElement(psiClass); + } + }); + + return new AlternativeSourceNotificationPanel(elems, baseClass, myProject, file); + } + return null; + } + + private static class ComboBoxClassElement { + private final PsiClass myClass; + private String myText; + + public ComboBoxClassElement(PsiClass aClass) { + myClass = aClass; + } + + @Override + public String toString() { + if (myText == null) { + Module module = ModuleUtilCore.findModuleForPsiElement(myClass); + if (module != null) { + myText = module.getName(); + } + else { + VirtualFile virtualFile = myClass.getContainingFile().getVirtualFile(); + final ProjectFileIndex index = ProjectRootManager.getInstance(myClass.getProject()).getFileIndex(); + VirtualFile root = index.getSourceRootForFile(virtualFile); + if (root == null) { + root = index.getClassRootForFile(virtualFile); + } + if (root != null) { + myText = root.getName(); + } + else { + myText = virtualFile.getPath(); + } + } + } + return myText; + } + } + + public static boolean fileProcessed(VirtualFile file) { + return FILE_PROCESSED_KEY.get(file) != null; + } + + private static class AlternativeSourceNotificationPanel extends EditorNotificationPanel { + public AlternativeSourceNotificationPanel(ComboBoxClassElement[] alternatives, + final PsiClass aClass, + final Project project, + final VirtualFile file) { + setText(DebuggerBundle.message("editor.notification.alternative.source", aClass.getQualifiedName())); + final ComboBox switcher = new ComboBox(alternatives); + switcher.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + FileEditorManager.getInstance(project).closeFile(file); + PsiClass item = ((ComboBoxClassElement)switcher.getSelectedItem()).myClass; + item = (PsiClass)item.getNavigationElement(); // go through compiled + DebuggerUtilsEx.setAlternativeSource(file, item.getContainingFile().getVirtualFile()); + item.navigate(true); + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null) { + session.updateExecutionPosition(); + } + } + }); + myLinksPanel.add(switcher); + } + } +} diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index 717c257fc5f3..cfe23b36e178 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -460,4 +460,5 @@ action.kill.process.description=Forcibly terminate debugged application evaluation.error.unknown.method.return.type=Cannot resolve method return type: {0} rule.name.group.by.class=Group by class rule.name.group.by.package=Group by package -error.context.has.changed=Context has changed, operation is not possible \ No newline at end of file +error.context.has.changed=Context has changed, operation is not possible +editor.notification.alternative.source=Alternative source available for the class {0} diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index 3f7e3b72f0d1..f5245a887992 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -267,6 +267,7 @@ +