mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-41229: attach source directly from edit window
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.codeInsight.daemon.impl;
|
||||
|
||||
import com.intellij.ProjectTopics;
|
||||
import com.intellij.ide.highlighter.JavaClassFileType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectBundle;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.compiled.ClsClassImpl;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.ui.EditorNotifications;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
*/
|
||||
public class AddSourcesNotificationProvider implements EditorNotifications.Provider<EditorNotificationPanel> {
|
||||
|
||||
private static final Key<EditorNotificationPanel> KEY = Key.create("add sources to class");
|
||||
|
||||
private final Project myProject;
|
||||
|
||||
public AddSourcesNotificationProvider(Project project, final EditorNotifications notifications) {
|
||||
myProject = project;
|
||||
myProject.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
|
||||
|
||||
public void beforeRootsChange(ModuleRootEvent event) {
|
||||
}
|
||||
|
||||
public void rootsChanged(ModuleRootEvent event) {
|
||||
notifications.updateAllNotifications();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Key<EditorNotificationPanel> getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
public EditorNotificationPanel createNotificationPanel(final VirtualFile file) {
|
||||
|
||||
if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
|
||||
final Library library = findLibrary(file);
|
||||
if (library == null) return null;
|
||||
|
||||
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
|
||||
if (!(psiFile instanceof PsiJavaFile)) return null;
|
||||
PsiClass[] classes = ((PsiJavaFile)psiFile).getClasses();
|
||||
if (classes.length == 0) return null;
|
||||
final String fqn = classes[0].getQualifiedName();
|
||||
if (fqn == null) return null;
|
||||
|
||||
final EditorNotificationPanel panel = new EditorNotificationPanel();
|
||||
panel.setText("Class sources not found");
|
||||
panel.createActionLabel("Attach sources", new Runnable() {
|
||||
public void run() {
|
||||
final Library library = findLibrary(file);
|
||||
if (library == null) {
|
||||
Messages.showErrorDialog(myProject, "Cannot find library for " + StringUtil.getShortName(fqn), "Error");
|
||||
return;
|
||||
}
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, true, false, true, true);
|
||||
descriptor.setTitle(ProjectBundle.message("library.attach.sources.action"));
|
||||
descriptor.setDescription(ProjectBundle.message("library.attach.sources.description"));
|
||||
VirtualFile[] roots = library.getFiles(OrderRootType.CLASSES);
|
||||
VirtualFile[] candidates = FileChooser.chooseFiles(myProject, descriptor, roots.length == 0 ? null : roots[0]);
|
||||
final VirtualFile[] files = PathUIUtils.scandAndSelectDetectedJavaSourceRoots(myProject, candidates);
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
Library.ModifiableModel model = library.getModifiableModel();
|
||||
for (VirtualFile virtualFile : files) {
|
||||
model.addRoot(virtualFile, OrderRootType.SOURCES);
|
||||
}
|
||||
model.commit();
|
||||
}
|
||||
});
|
||||
|
||||
PsiClass clsClass = JavaPsiFacade.getInstance(myProject).findClass(fqn, GlobalSearchScope.allScope(myProject));
|
||||
if (!(clsClass instanceof ClsClassImpl)) {
|
||||
return;
|
||||
}
|
||||
PsiClass sourceClass = ((ClsClassImpl)clsClass).getSourceMirrorClass();
|
||||
if (sourceClass == null) {
|
||||
return;
|
||||
}
|
||||
VirtualFile newFile = sourceClass.getContainingFile().getVirtualFile();
|
||||
assert newFile != null;
|
||||
|
||||
FileEditorManagerEx manager = FileEditorManagerEx.getInstanceEx(myProject);
|
||||
EditorWindow[] windows = manager.getWindows();
|
||||
for (EditorWindow window : windows) {
|
||||
int index = window.findFileIndex(file);
|
||||
if (index != -1) {
|
||||
manager.closeFile(file, window);
|
||||
try {
|
||||
newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, index);
|
||||
manager.openFile(newFile, true);
|
||||
}
|
||||
finally {
|
||||
newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Library findLibrary(VirtualFile file) {
|
||||
List<OrderEntry> entries = ProjectRootManager.getInstance(myProject).getFileIndex().getOrderEntriesForFile(file);
|
||||
for (OrderEntry entry : entries) {
|
||||
if (entry instanceof LibraryOrderEntry) {
|
||||
return ((LibraryOrderEntry)entry).getLibrary();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package com.intellij.openapi.roots.ui.configuration;
|
||||
|
||||
import com.intellij.ide.util.JavaUtilForVfs;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.configuration.libraryEditor.DetectedSourceRootsDialog;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -38,11 +39,11 @@ public class PathUIUtils {
|
||||
* This method takes a candidates for the project root, then scans the candidates and
|
||||
* if multiple candidates or non root source directories are found whithin some
|
||||
* directories, it shows a dialog that allows selecting or deselecting them.
|
||||
* @param component a parent component
|
||||
* @param parent a parent parent or project
|
||||
* @param rootCandidates a candidates for roots
|
||||
* @return a array of source folders or empty array if non was selected or dialog was canceled.
|
||||
*/
|
||||
public static VirtualFile[] scandAndSelectDetectedJavaSourceRoots(Component component, final VirtualFile[] rootCandidates) {
|
||||
public static VirtualFile[] scandAndSelectDetectedJavaSourceRoots(Object parent, final VirtualFile[] rootCandidates) {
|
||||
final Set<VirtualFile> result = new HashSet<VirtualFile>();
|
||||
final Map<VirtualFile, List<VirtualFile>> detectedRootsMap = new LinkedHashMap<VirtualFile, List<VirtualFile>>();
|
||||
// scan for roots
|
||||
@@ -59,7 +60,9 @@ public class PathUIUtils {
|
||||
}
|
||||
}, "Scanning for source roots", true, null);
|
||||
if(!detectedRootsMap.isEmpty()) {
|
||||
DetectedSourceRootsDialog dlg = new DetectedSourceRootsDialog(component, detectedRootsMap);
|
||||
DetectedSourceRootsDialog dlg = parent instanceof Component ?
|
||||
new DetectedSourceRootsDialog((Component)parent, detectedRootsMap) :
|
||||
new DetectedSourceRootsDialog((Project)parent, detectedRootsMap);
|
||||
dlg.show();
|
||||
if (dlg.isOK()) {
|
||||
result.addAll(dlg.getChosenRoots());
|
||||
|
||||
@@ -24,8 +24,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
import javax.swing.*;
|
||||
|
||||
public class JavaClassFileType implements FileType {
|
||||
|
||||
public static JavaClassFileType INSTANCE = new JavaClassFileType();
|
||||
|
||||
private static final Icon ICON = IconLoader.getIcon("/fileTypes/javaClass.png");
|
||||
|
||||
private JavaClassFileType() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return "CLASS";
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public class DefaultFileTypeFactory extends FileTypeFactory {
|
||||
public void createFileTypes(@NotNull final FileTypeConsumer consumer) {
|
||||
consumer.consume(new JavaClassFileType(), "class");
|
||||
consumer.consume(JavaClassFileType.INSTANCE, "class");
|
||||
|
||||
consumer.consume(JavaFileType.INSTANCE, "java");
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ import java.util.EventListener;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author dsl
|
||||
* @see com.intellij.openapi.roots.libraries.LibraryTablesRegistrar#getLibraryTable(com.intellij.openapi.project.Project)
|
||||
* @author dsl
|
||||
*/
|
||||
public interface LibraryTable {
|
||||
@NotNull
|
||||
|
||||
@@ -37,8 +37,10 @@ public class EditorNotificationPanel extends JPanel {
|
||||
|
||||
public EditorNotificationPanel() {
|
||||
super(new BorderLayout());
|
||||
|
||||
setBackground(LightColors.YELLOW);
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 4));
|
||||
setPreferredSize(new Dimension(-1, 24));
|
||||
add(myLabel, BorderLayout.CENTER);
|
||||
|
||||
myLinksPanel = new JPanel(new FlowLayout());
|
||||
@@ -51,11 +53,19 @@ public class EditorNotificationPanel extends JPanel {
|
||||
}
|
||||
|
||||
public HyperlinkLabel createActionLabel(final String text, @NonNls final String actionId) {
|
||||
return createActionLabel(text, new Runnable() {
|
||||
public void run() {
|
||||
executeAction(actionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public HyperlinkLabel createActionLabel(final String text, final Runnable action) {
|
||||
HyperlinkLabel label = new HyperlinkLabel(text, Color.BLUE, LightColors.YELLOW, Color.BLUE);
|
||||
label.addHyperlinkListener(new HyperlinkListener() {
|
||||
public void hyperlinkUpdate(final HyperlinkEvent e) {
|
||||
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
executeAction(actionId);
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ public class EditorNotifications extends AbstractProjectComponent {
|
||||
|
||||
public interface Provider<T extends JComponent> {
|
||||
|
||||
|
||||
Key<T> getKey();
|
||||
|
||||
@Nullable
|
||||
@@ -88,16 +89,23 @@ public class EditorNotifications extends AbstractProjectComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public void updateAllNotifications() {
|
||||
VirtualFile[] files = myFileEditorManager.getOpenFiles();
|
||||
for (VirtualFile file : files) {
|
||||
updateNotifications(file);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNotification(FileEditor editor, Key<? extends JComponent> key, @Nullable JComponent component) {
|
||||
JComponent old = editor.getUserData(key);
|
||||
if (old != null) {
|
||||
myFileEditorManager.removeTopComponent(editor, old);
|
||||
}
|
||||
if (component != null) {
|
||||
myFileEditorManager.addTopComponent(editor, component);
|
||||
editor.putUserData((Key<JComponent>)key, component);
|
||||
}
|
||||
else {
|
||||
JComponent old = editor.getUserData(key);
|
||||
if (old != null) {
|
||||
myFileEditorManager.removeTopComponent(editor, old);
|
||||
}
|
||||
editor.putUserData(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.ui.Splitter;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
@@ -57,6 +58,7 @@ public class EditorWindow {
|
||||
|
||||
private boolean myIsDisposed = false;
|
||||
private static final Icon PIN_ICON = IconLoader.getIcon("/nodes/tabPin.png");
|
||||
public static final Key<Integer> INITIAL_INDEX_KEY = Key.create("initial editor index");
|
||||
|
||||
protected EditorWindow(final EditorsSplitters owner) {
|
||||
myOwner = owner;
|
||||
@@ -404,7 +406,8 @@ public class EditorWindow {
|
||||
setSelectedEditor(editor, focusEditor);
|
||||
}
|
||||
else {
|
||||
final int indexToInsert = myTabbedPane.getSelectedIndex() + 1;
|
||||
Integer initialIndex = editor.getFile().getUserData(INITIAL_INDEX_KEY);
|
||||
final int indexToInsert = initialIndex == null ? myTabbedPane.getSelectedIndex() + 1 : initialIndex;
|
||||
final VirtualFile file = editor.getFile();
|
||||
final Icon template = IconLoader.getIcon("/fileTypes/text.png");
|
||||
myTabbedPane.insertTab(file, new EmptyIcon(template.getIconWidth(), template.getIconHeight()), new TComp(editor), null, indexToInsert);
|
||||
@@ -661,7 +664,7 @@ public class EditorWindow {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findFileIndex(final VirtualFile fileToFind) {
|
||||
public int findFileIndex(final VirtualFile fileToFind) {
|
||||
for (int i = 0; i != getTabCount(); ++i) {
|
||||
final VirtualFile file = getFileAt(i);
|
||||
if (file.equals (fileToFind)) {
|
||||
|
||||
+20
-20
@@ -25,7 +25,6 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.ui.InplaceButton;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collections;
|
||||
@@ -50,13 +49,26 @@ public class ChangelistConflictNotificationPanel extends EditorNotificationPanel
|
||||
myChangeList = manager.getChangeList(myChange);
|
||||
assert myChangeList != null;
|
||||
myLabel.setText("File from non-active changelist is modified");
|
||||
createActionLabel("Move changes", "move").
|
||||
setToolTipText("Move changes to active changelist (" + manager.getDefaultChangeList().getName() + ")");
|
||||
createActionLabel("Switch changelist", "switch").
|
||||
setToolTipText("Set active changelist to '" + myChangeList.getName() + "'");
|
||||
createActionLabel("Ignore", "ignore").
|
||||
setToolTipText("Hide this notification");
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
|
||||
createActionLabel("Move changes", new Runnable() {
|
||||
public void run() {
|
||||
ChangelistConflictResolution.MOVE.resolveConflict(myTracker.getProject(), myChangeList.getChanges());
|
||||
}
|
||||
}).setToolTipText("Move changes to active changelist (" + manager.getDefaultChangeList().getName() + ")");
|
||||
|
||||
createActionLabel("Switch changelist", new Runnable() {
|
||||
public void run() {
|
||||
List<Change> changes = Collections.singletonList(myTracker.getChangeListManager().getChange(myFile));
|
||||
ChangelistConflictResolution.SWITCH.resolveConflict(myTracker.getProject(), changes);
|
||||
}
|
||||
}).setToolTipText("Set active changelist to '" + myChangeList.getName() + "'");
|
||||
|
||||
createActionLabel("Ignore", new Runnable() {
|
||||
public void run() {
|
||||
myTracker.ignoreConflict(myFile, true);
|
||||
}
|
||||
}).setToolTipText("Hide this notification");
|
||||
|
||||
// setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
|
||||
|
||||
myLinksPanel.add(new InplaceButton("Show options dialog", IconLoader.getIcon("/general/ideOptions.png"), new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@@ -66,16 +78,4 @@ public class ChangelistConflictNotificationPanel extends EditorNotificationPanel
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeAction(String actionId) {
|
||||
if (actionId.equals("move")) {
|
||||
ChangelistConflictResolution.MOVE.resolveConflict(myTracker.getProject(), myChangeList.getChanges());
|
||||
} else if (actionId.equals("switch")) {
|
||||
List<Change> changes = Collections.singletonList(myTracker.getChangeListManager().getChange(myFile));
|
||||
ChangelistConflictResolution.SWITCH.resolveConflict(myTracker.getProject(), changes);
|
||||
} else if (actionId.equals("ignore")) {
|
||||
myTracker.ignoreConflict(myFile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,6 +668,8 @@
|
||||
|
||||
<fileType.fileViewProviderFactory filetype="CLASS" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||
|
||||
<editorNotificationProvider implementation="com.intellij.codeInsight.daemon.impl.AddSourcesNotificationProvider"/>
|
||||
|
||||
<lang.psiStructureViewFactory language="JAVA" implementationClass="com.intellij.lang.java.JavaStructureViewBuilderFactory"/>
|
||||
|
||||
<lang.ast.factory language="JAVA" implementationClass="com.intellij.psi.impl.source.tree.JavaASTFactory"/>
|
||||
|
||||
Reference in New Issue
Block a user