diff --git a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java index 9050d7ab6ecf..e4bdc0fb422a 100644 --- a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java +++ b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java @@ -19,7 +19,10 @@ import com.intellij.ProjectTopics; import com.intellij.codeEditor.JavaEditorFileSwapper; import com.intellij.codeInsight.AttachSourcesProvider; import com.intellij.ide.highlighter.JavaClassFileType; +import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileChooser.FileChooser; @@ -38,13 +41,13 @@ import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; import com.intellij.ui.GuiUtils; -import com.intellij.util.containers.*; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -92,9 +95,17 @@ public class AttachSourcesNotificationProvider implements EditorNotifications.Pr if (JavaEditorFileSwapper.findSourceFile(myProject, file) != null) return null; final EditorNotificationPanel panel = new EditorNotificationPanel(); - panel.setText(ProjectBundle.message("library.sources.not.found")); + final VirtualFile sourceFile = findSourceFile(file); - final AttachSourcesProvider.AttachSourcesAction defaultAction = createDefaultAction(); + final AttachSourcesProvider.AttachSourcesAction defaultAction; + if (sourceFile != null) { + panel.setText(ProjectBundle.message("library.sources.not.attached")); + defaultAction = new AttachJarAsSourcesAction(file, sourceFile, myProject); + } + else { + panel.setText(ProjectBundle.message("library.sources.not.found")); + defaultAction = new ChooseAndAttachSourcesAction(myProject); + } TreeSet actions = new TreeSet( new Comparator() { @@ -140,73 +151,15 @@ public class AttachSourcesNotificationProvider implements EditorNotifications.Pr return panel; } - private AttachSourcesProvider.AttachSourcesAction createDefaultAction() { - return new AttachSourcesProvider.AttachSourcesAction() { - public String getName() { - return ProjectBundle.message("module.libraries.attach.sources.button"); - } - - public String getBusyText() { - return ProjectBundle.message("library.attach.sources.action.busy.text"); - } - - public ActionCallback perform(final List libraries) { - 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")); - final Library firstLibrary = libraries.get(0).getLibrary(); - VirtualFile[] roots = firstLibrary != null ? firstLibrary.getFiles(OrderRootType.CLASSES) : VirtualFile.EMPTY_ARRAY; - VirtualFile[] candidates = FileChooser.chooseFiles(myProject, descriptor, roots.length == 0 ? null : roots[0]); - final VirtualFile[] files = PathUIUtils.scanAndSelectDetectedJavaSourceRoots(myProject, candidates); - if (files.length == 0) { - return new ActionCallback.Rejected(); - } - final Map librariesToAppendSourcesTo = new HashMap(); - for (LibraryOrderEntry library : libraries) { - librariesToAppendSourcesTo.put(library.getLibrary(), library); - } - if (librariesToAppendSourcesTo.size() == 1) { - appendSources(firstLibrary, files); - } else { - librariesToAppendSourcesTo.put(null, null); - final Collection orderEntries = librariesToAppendSourcesTo.values(); - JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep("Multiple libraries contain file.
Choose libraries to attach sources to", - orderEntries.toArray(new LibraryOrderEntry[orderEntries.size()])){ - @Override - public ListSeparator getSeparatorAbove(LibraryOrderEntry value) { - return value == null ? new ListSeparator() : null; - } - - @NotNull - @Override - public String getTextFor(LibraryOrderEntry value) { - if (value != null) { - return value.getPresentableName() + " (" + value.getOwnerModule().getName() + ")"; - } - else { - return "All"; - } - } - - @Override - public PopupStep onChosen(LibraryOrderEntry libraryOrderEntry, boolean finalChoice) { - if (libraryOrderEntry != null) { - appendSources(libraryOrderEntry.getLibrary(), files); - } else { - for (Library libOrderEntry : librariesToAppendSourcesTo.keySet()) { - if (libOrderEntry != null) { - appendSources(libOrderEntry, files); - } - } - } - return FINAL_CHOICE; - } - }).showCenteredInCurrentWindow(myProject); - } - - return new ActionCallback.Done(); - } - }; + @Nullable + private static VirtualFile findSourceFile(VirtualFile classFile) { + final VirtualFile parent = classFile.getParent(); + String name = classFile.getName(); + int i = name.indexOf('$'); + if (i != -1) name = name.substring(0, i); + i = name.indexOf('.'); + if (i != -1) name = name.substring(0, i); + return parent.findChild(name + JavaFileType.DOT_DEFAULT_EXTENSION); } private static void appendSources(final Library library, final VirtualFile[] files) { @@ -232,4 +185,131 @@ public class AttachSourcesNotificationProvider implements EditorNotifications.Pr } return libs.isEmpty() ? null : libs; } + + private static class AttachJarAsSourcesAction implements AttachSourcesProvider.AttachSourcesAction { + private final VirtualFile myClassFile; + private final VirtualFile mySourceFile; + private final Project myProject; + + public AttachJarAsSourcesAction(VirtualFile classFile, VirtualFile sourceFile, Project project) { + myClassFile = classFile; + mySourceFile = sourceFile; + myProject = project; + } + + public String getName() { + return ProjectBundle.message("module.libraries.attach.sources.immediately.button"); + } + + public String getBusyText() { + return ProjectBundle.message("library.attach.sources.action.busy.text"); + } + + @Override + public ActionCallback perform(List orderEntriesContainingFile) { + final List modelsToCommit = new ArrayList(); + for (LibraryOrderEntry orderEntry : orderEntriesContainingFile) { + final Library library = orderEntry.getLibrary(); + if (library == null) continue; + final VirtualFile root = findRoot(library); + if (root == null) continue; + final Library.ModifiableModel model = library.getModifiableModel(); + model.addRoot(root, OrderRootType.SOURCES); + modelsToCommit.add(model); + } + if (modelsToCommit.isEmpty()) return new ActionCallback.Rejected(); + new WriteAction() { + protected void run(final Result result) { + for (Library.ModifiableModel model : modelsToCommit) { + model.commit(); + } + } + }.execute(); + + return new ActionCallback.Done(); + } + + @Nullable + private VirtualFile findRoot(Library library) { + for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) { + if (VfsUtil.isAncestor(classesRoot, myClassFile, true)) { + return classesRoot; + } + } + return null; + } + } + + private static class ChooseAndAttachSourcesAction implements AttachSourcesProvider.AttachSourcesAction { + private Project myProject; + + public ChooseAndAttachSourcesAction(Project project) { + myProject = project; + } + + public String getName() { + return ProjectBundle.message("module.libraries.attach.sources.button"); + } + + public String getBusyText() { + return ProjectBundle.message("library.attach.sources.action.busy.text"); + } + + public ActionCallback perform(final List libraries) { + 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")); + final Library firstLibrary = libraries.get(0).getLibrary(); + VirtualFile[] roots = firstLibrary != null ? firstLibrary.getFiles(OrderRootType.CLASSES) : VirtualFile.EMPTY_ARRAY; + VirtualFile[] candidates = FileChooser.chooseFiles(myProject, descriptor, roots.length == 0 ? null : roots[0]); + final VirtualFile[] files = PathUIUtils.scanAndSelectDetectedJavaSourceRoots(myProject, candidates); + if (files.length == 0) { + return new ActionCallback.Rejected(); + } + final Map librariesToAppendSourcesTo = new HashMap(); + for (LibraryOrderEntry library : libraries) { + librariesToAppendSourcesTo.put(library.getLibrary(), library); + } + if (librariesToAppendSourcesTo.size() == 1) { + appendSources(firstLibrary, files); + } else { + librariesToAppendSourcesTo.put(null, null); + final Collection orderEntries = librariesToAppendSourcesTo.values(); + JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep("Multiple libraries contain file.
Choose libraries to attach sources to", + orderEntries.toArray(new LibraryOrderEntry[orderEntries.size()])){ + @Override + public ListSeparator getSeparatorAbove(LibraryOrderEntry value) { + return value == null ? new ListSeparator() : null; + } + + @NotNull + @Override + public String getTextFor(LibraryOrderEntry value) { + if (value != null) { + return value.getPresentableName() + " (" + value.getOwnerModule().getName() + ")"; + } + else { + return "All"; + } + } + + @Override + public PopupStep onChosen(LibraryOrderEntry libraryOrderEntry, boolean finalChoice) { + if (libraryOrderEntry != null) { + appendSources(libraryOrderEntry.getLibrary(), files); + } else { + for (Library libOrderEntry : librariesToAppendSourcesTo.keySet()) { + if (libOrderEntry != null) { + appendSources(libOrderEntry, files); + } + } + } + return FINAL_CHOICE; + } + }).showCenteredInCurrentWindow(myProject); + } + + return new ActionCallback.Done(); + } + } } diff --git a/platform/platform-resources-en/src/messages/ProjectBundle.properties b/platform/platform-resources-en/src/messages/ProjectBundle.properties index aa1b8bebc732..c252bf904bae 100644 --- a/platform/platform-resources-en/src/messages/ProjectBundle.properties +++ b/platform/platform-resources-en/src/messages/ProjectBundle.properties @@ -159,6 +159,7 @@ module.libraries.remove.button=R&emove module.libraries.javadoc.attach.button=Attach D&ocumentation... module.libraries.attach.sources.button=Attach &Sources... module.libraries.attach.classes.button=Attach &Classes... +module.libraries.attach.sources.immediately.button=Attach &Sources module.libraries.attach.more.button=Attach &More... attach.annotations.button=Attach Anno&tations... module.libraries.add.button=&Add Library... @@ -170,6 +171,7 @@ library.attach.jar.directory.to.library.action=Attach Jar Directories to Library library.attach.jar.directory.action=Attach Jar Directories library.attach.jar.directory.description=Select directories in which jar/zip files are located library.sources.not.found=Sources not found +library.sources.not.attached=Sources not attached library.attach.sources.action=Attach Sources library.attach.sources.action.busy.text=Attaching... library.attach.sources.description=Select jar/zip files or directories in which library sources are located