diff options
Diffstat (limited to 'platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java')
-rw-r--r-- | platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java | 248 |
1 files changed, 141 insertions, 107 deletions
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; |