diff options
Diffstat (limited to 'platform/lang-impl/src/com/intellij/codeInsight/documentation')
5 files changed, 330 insertions, 163 deletions
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java index 83f17299987b..e0d17d3c164b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java @@ -23,7 +23,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowType; @@ -34,6 +34,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.util.PsiUtilBase; import com.intellij.ui.content.*; +import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; @@ -136,30 +137,33 @@ public abstract class DockablePopupManager<T extends JComponent & Disposable> { protected AnAction[] createActions() { - return new AnAction[]{ - new ToggleAction(getAutoUpdateTitle(), getAutoUpdateDescription(), - AllIcons.General.AutoscrollFromSource) { - @Override - public boolean isSelected(AnActionEvent e) { - return myAutoUpdateDocumentation; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - PropertiesComponent.getInstance().setValue(getAutoUpdateEnabledProperty(), String.valueOf(state)); - myAutoUpdateDocumentation = state; - restartAutoUpdate(state); - } - }, - new AnAction("Restore Popup", getRestorePopupDescription(), AllIcons.Actions.Cancel) { - @Override - public void actionPerformed(AnActionEvent e) { - restorePopupBehavior(); - } - }}; + ToggleAction toggleAutoUpdateAction = new ToggleAction(getAutoUpdateTitle(), getAutoUpdateDescription(), + AllIcons.General.AutoscrollFromSource) { + @Override + public boolean isSelected(AnActionEvent e) { + return myAutoUpdateDocumentation; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + PropertiesComponent.getInstance().setValue(getAutoUpdateEnabledProperty(), String.valueOf(state)); + myAutoUpdateDocumentation = state; + restartAutoUpdate(state); + } + }; + return new AnAction[]{toggleAutoUpdateAction, createRestorePopupAction()}; + } + + @NotNull + protected AnAction createRestorePopupAction() { + return new AnAction("Restore Popup", getRestorePopupDescription(), AllIcons.Actions.Cancel) { + @Override + public void actionPerformed(AnActionEvent e) { + restorePopupBehavior(); + } + }; } - protected void restartAutoUpdate(final boolean state) { if (state && myToolWindow != null) { if (myAutoUpdateRequest == null) { @@ -189,12 +193,16 @@ public abstract class DockablePopupManager<T extends JComponent & Disposable> { public void updateComponent() { if (myProject.isDisposed()) return; - AsyncResult<DataContext> asyncResult = DataManager.getInstance().getDataContextFromFocus(); - DataContext dataContext = asyncResult.getResult(); - if (dataContext == null) { - return; - } + DataManager.getInstance().getDataContextFromFocus().doWhenDone(new Consumer<DataContext>() { + @Override + public void consume(@NotNull DataContext dataContext) { + if (!myProject.isOpen()) return; + updateComponentInner(dataContext); + } + }); + } + private void updateComponentInner(@NotNull DataContext dataContext) { if (CommonDataKeys.PROJECT.getData(dataContext) != myProject) { return; } @@ -228,13 +236,10 @@ public abstract class DockablePopupManager<T extends JComponent & Disposable> { protected void restorePopupBehavior() { if (myToolWindow != null) { PropertiesComponent.getInstance().setValue(getShowInToolWindowProperty(), Boolean.FALSE.toString()); - - final Content[] contents = myToolWindow.getContentManager().getContents(); - for (final Content content : contents) { - myToolWindow.getContentManager().removeContent(content, true); - } - - ToolWindowManagerEx.getInstanceEx(myProject).unregisterToolWindow(getToolwindowId()); + ToolWindowManagerEx toolWindowManagerEx = ToolWindowManagerEx.getInstanceEx(myProject); + toolWindowManagerEx.hideToolWindow(getToolwindowId(), false); + toolWindowManagerEx.unregisterToolWindow(getToolwindowId()); + Disposer.dispose(myToolWindow.getContentManager()); myToolWindow = null; restartAutoUpdate(false); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index cf9b5ad22798..50f69088033b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -32,6 +32,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.impl.ActionButton; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; @@ -47,6 +48,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.JBColor; import com.intellij.ui.SideBorder; import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.components.JBScrollPane; @@ -66,10 +68,9 @@ import javax.swing.event.HyperlinkListener; import javax.swing.text.*; import java.awt.*; import java.awt.event.*; -import java.util.Collections; +import java.net.URL; +import java.util.*; import java.util.List; -import java.util.Map; -import java.util.Stack; public class DocumentationComponent extends JPanel implements Disposable, DataProvider { @@ -92,7 +93,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr private final Stack<Context> myBackStack = new Stack<Context>(); private final Stack<Context> myForwardStack = new Stack<Context>(); private final ActionToolbar myToolBar; - private boolean myIsEmpty; + private volatile boolean myIsEmpty; private boolean myIsShown; private final JLabel myElementLabel; private Style myFontSizeStyle; @@ -101,6 +102,13 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr private final MyShowSettingsButton myShowSettingsButton; private boolean myIgnoreFontSizeSliderChange; private String myEffectiveExternalUrl; + private final MyDictionary<String, Image> myImageProvider = new MyDictionary<String, Image>() { + @Override + public Image get(Object key) { + PsiElement element = getElement(); + return element == null ? null : myManager.getElementImage(element, ((URL)key).toExternalForm()); + } + }; private static class Context { final SmartPsiElementPointer element; @@ -178,6 +186,14 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr GraphicsUtil.setupAntialiasing(g); super.paintComponent(g); } + + @Override + public void setDocument(Document doc) { + super.setDocument(doc); + if (doc instanceof StyledDocument) { + doc.putProperty("imageCache", myImageProvider); + } + } }; DataProvider helpDataProvider = new DataProvider() { @Override @@ -308,6 +324,10 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr if (additionalActions != null) { for (final AnAction action : additionalActions) { actions.add(action); + ShortcutSet shortcutSet = action.getShortcutSet(); + if (shortcutSet != null) { + action.registerCustomShortcutSet(shortcutSet, this); + } } } @@ -378,7 +398,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr myFontSizeSlider.setSnapToTicks(true); UIUtil.setSliderIsFilled(myFontSizeSlider, true); result.add(myFontSizeSlider); - result.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor(), 1)); + result.setBorder(BorderFactory.createLineBorder(JBColor.border(), 1)); myFontSizeSlider.addChangeListener(new ChangeListener() { @Override @@ -418,11 +438,11 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr } } - public synchronized boolean isEmpty() { + public boolean isEmpty() { return myIsEmpty; } - public synchronized void startWait() { + public void startWait() { myIsEmpty = true; } @@ -473,7 +493,8 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr } public void replaceText(String text, PsiElement element) { - if (element == null || getElement() != element) return; + PsiElement current = getElement(); + if (current == null || !current.getManager().areElementsEquivalent(current, element)) return; setText(text, element, false); if (!myBackStack.empty()) myBackStack.pop(); } @@ -530,6 +551,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr myText = text; } + //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -544,7 +566,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr return; } - StyledDocument styledDocument = (StyledDocument)document; + final StyledDocument styledDocument = (StyledDocument)document; if (myFontSizeStyle == null) { myFontSizeStyle = styledDocument.addStyle("active", null); } @@ -555,7 +577,14 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr if (Registry.is("documentation.component.editor.font")) { StyleConstants.setFontFamily(myFontSizeStyle, scheme.getEditorFontName()); } - styledDocument.setCharacterAttributes(0, document.getLength(), myFontSizeStyle, false); + + final Style sizeStyle = myFontSizeStyle; + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + styledDocument.setCharacterAttributes(0, styledDocument.getLength(), sizeStyle, false); + } + }); } private void goBack() { @@ -631,15 +660,10 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr private class EditDocumentationSourceAction extends BaseNavigateToSourceAction { - protected EditDocumentationSourceAction() { + EditDocumentationSourceAction() { super(true); - } - - @Override - public void update(AnActionEvent event) { - super.update(event); - event.getPresentation().setIcon(AllIcons.Actions.EditSource); - event.getPresentation().setText("Edit Source"); + getTemplatePresentation().setIcon(AllIcons.Actions.EditSource); + getTemplatePresentation().setText("Edit Source"); } @Override @@ -678,8 +702,8 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr final PsiElement originalElement = DocumentationManager.getOriginalElement(element); boolean processed = false; if (provider instanceof CompositeDocumentationProvider) { - for (final DocumentationProvider documentationProvider : ((CompositeDocumentationProvider)provider).getProviders()) { - if (documentationProvider instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)documentationProvider).handleExternal(element, originalElement)) { + for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { + if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { processed = true; break; } @@ -874,4 +898,36 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr mySettingsPanel.setVisible(true); } } + + private static abstract class MyDictionary<K, V> extends Dictionary<K, V> { + @Override + public int size() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + throw new UnsupportedOperationException(); + } + + @Override + public Enumeration<K> keys() { + throw new UnsupportedOperationException(); + } + + @Override + public Enumeration<V> elements() { + throw new UnsupportedOperationException(); + } + + @Override + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 05a1e01c6aa1..63484f890225 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -36,9 +36,11 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.AnActionListener; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEntry; @@ -58,13 +60,13 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.ListScrollingUtil; import com.intellij.ui.content.Content; import com.intellij.ui.popup.AbstractPopup; -import com.intellij.ui.popup.NotLookupOrSearchCondition; import com.intellij.ui.popup.PopupPositionManager; import com.intellij.ui.popup.PopupUpdateProcessor; import com.intellij.util.Alarm; import com.intellij.util.BooleanFunction; import com.intellij.util.Consumer; import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -125,6 +127,24 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp return "Auto Show Documentation for Selected Element"; } + @NotNull + @Override + protected AnAction createRestorePopupAction() { + AnAction restorePopupAction = super.createRestorePopupAction(); + ShortcutSet quickDocShortcut = ActionManager.getInstance().getAction(IdeActions.ACTION_QUICK_JAVADOC).getShortcutSet(); + restorePopupAction.registerCustomShortcutSet(quickDocShortcut, null); + return restorePopupAction; + } + + @Override + protected void restorePopupBehavior() { + if (myPreviouslyFocused != null) { + IdeFocusManager.getInstance(myProject).requestFocus(myPreviouslyFocused, true); + } + super.restorePopupBehavior(); + updateComponent(); + } + /** * @return <code>true</code> if quick doc control is configured to not prevent user-IDE interaction (e.g. should be closed if * the user presses a key); @@ -165,7 +185,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp @Override public void beforeEditorTyping(char c, DataContext dataContext) { final JBPopup hint = getDocInfoHint(); - if (hint != null) { + if (hint != null && LookupManager.getActiveLookup(myEditor) == null) { hint.cancel(); } } @@ -213,7 +233,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp } public void showJavaDocInfo(@NotNull final PsiElement element, final PsiElement original) { - showJavaDocInfo(element, original, false, null); + showJavaDocInfo(element, original, null); } /** @@ -229,35 +249,31 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp * two possible situations - the quick doc is shown automatically on mouse over element; the quick doc is shown * on explicit action call (Ctrl+Q). We want to close the doc on, say, editor viewport position change * at the first situation but don't want to do that at the second - * @param allowReuse defines whether currently requested documentation should reuse existing doc control (if any) */ public void showJavaDocInfo(@NotNull Editor editor, @NotNull final PsiElement element, @NotNull final PsiElement original, @Nullable Runnable closeCallback, - boolean closeOnSneeze, - boolean allowReuse) + boolean closeOnSneeze) { myEditor = editor; myCloseOnSneeze = closeOnSneeze; - showJavaDocInfo(element, original, allowReuse, closeCallback); + showJavaDocInfo(element, original, closeCallback); } public void showJavaDocInfo(@NotNull final PsiElement element, final PsiElement original, - boolean allowReuse, - @Nullable Runnable closeCallback) - { + @Nullable Runnable closeCallback) { PopupUpdateProcessor updateProcessor = new PopupUpdateProcessor(element.getProject()) { @Override public void updatePopup(Object lookupItemObject) { if (lookupItemObject instanceof PsiElement) { - doShowJavaDocInfo((PsiElement)lookupItemObject, false, this, original, false); + doShowJavaDocInfo((PsiElement)lookupItemObject, false, this, original, null); } } }; - doShowJavaDocInfo(element, false, updateProcessor, original, allowReuse, closeCallback); + doShowJavaDocInfo(element, false, updateProcessor, original, closeCallback); } public void showJavaDocInfo(final Editor editor, @Nullable final PsiFile file, boolean requestFocus) { @@ -267,14 +283,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp public void showJavaDocInfo(final Editor editor, @Nullable final PsiFile file, boolean requestFocus, - final Runnable closeCallback) { - showJavaDocInfo(editor, file, requestFocus, true, closeCallback); - } - - private void showJavaDocInfo(final Editor editor, - @Nullable final PsiFile file, - boolean requestFocus, - final boolean autoupdate, @Nullable final Runnable closeCallback) { + @Nullable final Runnable closeCallback) { myEditor = editor; final Project project = getProject(file); PsiDocumentManager.getInstance(project).commitAllDocuments(); @@ -324,7 +333,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp return; } if (lookupIteObject instanceof PsiElement) { - doShowJavaDocInfo((PsiElement)lookupIteObject, false, this, originalElement, autoupdate, closeCallback); + doShowJavaDocInfo((PsiElement)lookupIteObject, false, this, originalElement, closeCallback); return; } @@ -347,12 +356,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp } } else { - doShowJavaDocInfo(element, false, this, originalElement, autoupdate, closeCallback); + doShowJavaDocInfo(element, false, this, originalElement, closeCallback); } } }; - doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, autoupdate, closeCallback); + doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, closeCallback); } public PsiElement findTargetElement(Editor editor, PsiFile file) { @@ -363,54 +372,56 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp return file != null ? file.findElementAt(editor.getCaretModel().getOffset()) : null; } - private void doShowJavaDocInfo(final PsiElement element, boolean requestFocus, PopupUpdateProcessor updateProcessor, - final PsiElement originalElement, final boolean autoupdate) - { - doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, autoupdate, null); - } - private void doShowJavaDocInfo(@NotNull final PsiElement element, boolean requestFocus, PopupUpdateProcessor updateProcessor, final PsiElement originalElement, - final boolean allowReuse, - @Nullable final Runnable closeCallback) - { + @Nullable final Runnable closeCallback) { Project project = getProject(element); storeOriginalElement(project, originalElement, element); + myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project); + + JBPopup _oldHint = getDocInfoHint(); if (myToolWindow == null && PropertiesComponent.getInstance().isTrueValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW)) { createToolWindow(element, originalElement); - return; } else if (myToolWindow != null) { - if (allowReuse && !myToolWindow.isAutoHide()) { - final Content content = myToolWindow.getContentManager().getSelectedContent(); - if (content != null) { - final DocumentationComponent component = (DocumentationComponent)content.getComponent(); - if (component.getElement() != element) { - content.setDisplayName(getTitle(element, true)); - fetchDocInfo(getDefaultCollector(element, originalElement), component, true); + Content content = myToolWindow.getContentManager().getSelectedContent(); + if (content != null) { + DocumentationComponent component = (DocumentationComponent)content.getComponent(); + if (element.getManager().areElementsEquivalent(component.getElement(), element)) { + JComponent preferredFocusableComponent = content.getPreferredFocusableComponent(); + // focus toolwindow on the second actionPerformed + boolean focus = requestFocus || CommandProcessor.getInstance().getCurrentCommand() != null; + if (preferredFocusableComponent != null && focus) { + IdeFocusManager.getInstance(myProject).requestFocus(preferredFocusableComponent, true); } } - - if (!myToolWindow.isVisible()) { - myToolWindow.show(null); + else { + content.setDisplayName(getTitle(element, true)); + fetchDocInfo(getDefaultCollector(element, originalElement), component, true); } - return; } - else { - restorePopupBehavior(); + + if (!myToolWindow.isVisible()) { + myToolWindow.show(null); } } - - final JBPopup _oldHint = getDocInfoHint(); - if (_oldHint != null && _oldHint.isVisible() && _oldHint instanceof AbstractPopup) { - final DocumentationComponent oldComponent = (DocumentationComponent)((AbstractPopup)_oldHint).getComponent(); + else if (_oldHint != null && _oldHint.isVisible() && _oldHint instanceof AbstractPopup) { + DocumentationComponent oldComponent = (DocumentationComponent)((AbstractPopup)_oldHint).getComponent(); fetchDocInfo(getDefaultCollector(element, originalElement), oldComponent); - return; } + else { + showInPopup(element, requestFocus, updateProcessor, originalElement, closeCallback); + } + } + private void showInPopup(@NotNull final PsiElement element, + boolean requestFocus, + PopupUpdateProcessor updateProcessor, + final PsiElement originalElement, + @Nullable final Runnable closeCallback) { final DocumentationComponent component = new DocumentationComponent(this); component.setNavigateCallback(new Consumer<PsiElement>() { @Override @@ -426,26 +437,30 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp @Override public boolean process(JBPopup popup) { createToolWindow(element, originalElement); + myToolWindow.setAutoHide(false); popup.cancel(); return false; } }; - final KeyboardShortcut keyboardShortcut = ActionManagerEx.getInstanceEx().getKeyboardShortcut("QuickJavaDoc"); - final List<Pair<ActionListener, KeyStroke>> actions = - Collections.singletonList(Pair.<ActionListener, KeyStroke>create(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - createToolWindow(element, originalElement); - final JBPopup hint = getDocInfoHint(); - if (hint != null && hint.isVisible()) hint.cancel(); - } - }, keyboardShortcut != null ? keyboardShortcut.getFirstKeyStroke() : null)); // Null keyStroke is ok here + ActionListener actionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + createToolWindow(element, originalElement); + final JBPopup hint = getDocInfoHint(); + if (hint != null && hint.isVisible()) hint.cancel(); + } + }; + List<Pair<ActionListener, KeyStroke>> actions = ContainerUtil.newSmartList(); + AnAction quickDocAction = ActionManagerEx.getInstanceEx().getAction(IdeActions.ACTION_QUICK_JAVADOC); + for (Shortcut shortcut : quickDocAction.getShortcutSet().getShortcuts()) { + if (!(shortcut instanceof KeyboardShortcut)) continue; + actions.add(Pair.create(actionListener, ((KeyboardShortcut)shortcut).getFirstKeyStroke())); + } boolean hasLookup = LookupManager.getActiveLookup(myEditor) != null; final JBPopup hint = JBPopupFactory.getInstance().createComponentPopupBuilder(component, component) - .setRequestFocusCondition(project, NotLookupOrSearchCondition.INSTANCE) - .setProject(project) + .setProject(element.getProject()) .addListener(updateProcessor) .addUserData(updateProcessor) .setKeyboardActions(actions) @@ -490,20 +505,6 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp }) .createPopup(); - - AbstractPopup oldHint = (AbstractPopup)getDocInfoHint(); - if (oldHint != null) { - DocumentationComponent oldComponent = (DocumentationComponent)oldHint.getComponent(); - PsiElement element1 = oldComponent.getElement(); - if (Comparing.equal(element, element1)) { - if (requestFocus) { - component.getComponent().requestFocus(); - } - return; - } - oldHint.cancel(); - } - component.setHint(hint); if (myEditor == null) { @@ -515,7 +516,6 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp fetchDocInfo(getDefaultCollector(element, originalElement), component); myDocInfoHintRef = new WeakReference<JBPopup>(hint); - myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project); if (fromQuickSearch() && myPreviouslyFocused != null) { ((ChooseByNameBase.JPanelProvider)myPreviouslyFocused.getParent()).registerHint(hint); @@ -666,11 +666,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp private ActionCallback doFetchDocInfo(final DocumentationComponent component, final DocumentationCollector provider, final boolean cancelRequests, final boolean clearHistory) { final ActionCallback callback = new ActionCallback(); + boolean wasEmpty = component.isEmpty(); component.startWait(); if (cancelRequests) { myUpdateDocAlarm.cancelAllRequests(); } - if (component.isEmpty()) { + if (wasEmpty) { component.setText(CodeInsightBundle.message("javadoc.fetching.progress"), null, clearHistory); final AbstractPopup jbPopup = (AbstractPopup)getDocInfoHint(); if (jbPopup != null) { @@ -845,7 +846,26 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp } else if (url.startsWith(PSI_ELEMENT_PROTOCOL)) { final String refText = url.substring(PSI_ELEMENT_PROTOCOL.length()); DocumentationProvider provider = getProviderFromElement(psiElement); - final PsiElement targetElement = provider.getDocumentationElementForLink(manager, refText, psiElement); + PsiElement targetElement = provider.getDocumentationElementForLink(manager, refText, psiElement); + if (targetElement == null) { + for (DocumentationProvider documentationProvider : Extensions.getExtensions(DocumentationProvider.EP_NAME)) { + targetElement = documentationProvider.getDocumentationElementForLink(manager, refText, psiElement); + if (targetElement != null) { + break; + } + } + } + if (targetElement == null) { + for (Language language : Language.getRegisteredLanguages()) { + DocumentationProvider documentationProvider = LanguageDocumentation.INSTANCE.forLanguage(language); + if (documentationProvider != null) { + targetElement = documentationProvider.getDocumentationElementForLink(manager, refText, psiElement); + if (targetElement != null) { + break; + } + } + } + } if (targetElement != null) { fetchDocInfo(getDefaultCollector(targetElement, null), component); } @@ -854,33 +874,33 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp final DocumentationProvider provider = getProviderFromElement(psiElement); boolean processed = false; if (provider instanceof CompositeDocumentationProvider) { - for (DocumentationProvider documentationProvider : ((CompositeDocumentationProvider)provider).getProviders()) { - if (documentationProvider instanceof ExternalDocumentationHandler) { - final ExternalDocumentationHandler externalDocumentationHandler = (ExternalDocumentationHandler)documentationProvider; - if (externalDocumentationHandler.canFetchDocumentationLink(url)) { - fetchDocInfo(new DocumentationCollector() { - @Override - public String getDocumentation() throws Exception { - return externalDocumentationHandler.fetchExternalDocumentation(url, psiElement); - } + for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { + if (!(p instanceof ExternalDocumentationHandler)) continue; - @Override - public PsiElement getElement() { - return psiElement; - } + final ExternalDocumentationHandler externalHandler = (ExternalDocumentationHandler)p; + if (externalHandler.canFetchDocumentationLink(url)) { + fetchDocInfo(new DocumentationCollector() { + @Override + public String getDocumentation() throws Exception { + return externalHandler.fetchExternalDocumentation(url, psiElement); + } - @Nullable - @Override - public String getEffectiveExternalUrl() { - return url; - } - }, component); - processed = true; - } - else if (externalDocumentationHandler.handleExternalLink(manager, url, psiElement)) { - processed = true; - break; - } + @Override + public PsiElement getElement() { + return psiElement; + } + + @Nullable + @Override + public String getEffectiveExternalUrl() { + return url; + } + }, component); + processed = true; + } + else if (externalHandler.handleExternalLink(manager, url, psiElement)) { + processed = true; + break; } } } @@ -979,12 +999,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp @Override protected void doUpdateComponent(Editor editor, PsiFile psiFile) { - showJavaDocInfo(editor, psiFile, false, true, null); + showJavaDocInfo(editor, psiFile, false, null); } @Override protected void doUpdateComponent(@NotNull PsiElement element) { - showJavaDocInfo(element, element, true, null); + showJavaDocInfo(element, element, null); } @Override @@ -992,6 +1012,20 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp return getTitle(element, true); } + @Nullable + public Image getElementImage(@NotNull PsiElement element, @NotNull String imageSpec) { + DocumentationProvider provider = getProviderFromElement(element); + if (provider instanceof CompositeDocumentationProvider) { + for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { + if (p instanceof DocumentationProviderEx) { + Image image = ((DocumentationProviderEx)p).getLocalImageForElement(element, imageSpec); + if (image != null) return image; + } + } + } + return null; + } + private interface DocumentationCollector { @Nullable String getDocumentation() throws Exception; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverManager.java index 47bf01f2e7c8..0cd18020ad54 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverManager.java @@ -301,7 +301,7 @@ public class QuickDocOnMouseOverManager { info.editor.putUserData(PopupFactoryImpl.ANCHOR_POPUP_POSITION, info.editor.offsetToVisualPosition(info.originalElement.getTextRange().getStartOffset())); try { - info.docManager.showJavaDocInfo(info.editor, info.targetElement, info.originalElement, myHintCloseCallback, true, true); + info.docManager.showJavaDocInfo(info.editor, info.targetElement, info.originalElement, myHintCloseCallback, true); myDocumentationManager = new WeakReference<DocumentationManager>(info.docManager); } finally { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocUtil.java new file mode 100644 index 000000000000..064bd020183b --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocUtil.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2014 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.documentation; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.PsiElement; +import com.intellij.ui.content.Content; +import com.intellij.ui.popup.AbstractPopup; +import com.intellij.util.Producer; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author gregsh + */ +public class QuickDocUtil { + + public static void updateQuickDocAsync(@NotNull final PsiElement element, @NotNull final Producer<String> docProducer) { + final Project project = element.getProject(); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + final String documentation = docProducer.produce(); + if (StringUtil.isEmpty(documentation)) return; + // modal dialogs with fragment editors fix: can't guess proper modality state here + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + DocumentationManager documentationManager = DocumentationManager.getInstance(project); + DocumentationComponent component; + JBPopup hint = documentationManager.getDocInfoHint(); + if (hint != null) { + component = (DocumentationComponent)((AbstractPopup)hint).getComponent(); + } + else if (documentationManager.hasActiveDockedDocWindow()) { + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.DOCUMENTATION); + Content selectedContent = toolWindow == null ? null : toolWindow.getContentManager().getSelectedContent(); + component = selectedContent == null ? null : (DocumentationComponent)selectedContent.getComponent(); + } + else { + component = null; + } + if (component != null) { + component.replaceText(documentation, element); + } + } + }); + } + }); + } +} |