2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.codeInsight
.documentation
;
19 import com
.intellij
.codeInsight
.CodeInsightBundle
;
20 import com
.intellij
.codeInsight
.TargetElementUtilBase
;
21 import com
.intellij
.codeInsight
.hint
.HintManagerImpl
;
22 import com
.intellij
.codeInsight
.hint
.ParameterInfoController
;
23 import com
.intellij
.codeInsight
.lookup
.Lookup
;
24 import com
.intellij
.codeInsight
.lookup
.LookupElement
;
25 import com
.intellij
.codeInsight
.lookup
.LookupManager
;
26 import com
.intellij
.ide
.BrowserUtil
;
27 import com
.intellij
.ide
.DataManager
;
28 import com
.intellij
.ide
.IdeEventQueue
;
29 import com
.intellij
.ide
.util
.PropertiesComponent
;
30 import com
.intellij
.ide
.util
.gotoByName
.ChooseByNameBase
;
31 import com
.intellij
.lang
.Language
;
32 import com
.intellij
.lang
.LanguageDocumentation
;
33 import com
.intellij
.lang
.documentation
.CompositeDocumentationProvider
;
34 import com
.intellij
.lang
.documentation
.DocumentationProvider
;
35 import com
.intellij
.openapi
.actionSystem
.*;
36 import com
.intellij
.openapi
.actionSystem
.ex
.ActionManagerEx
;
37 import com
.intellij
.openapi
.actionSystem
.ex
.AnActionListener
;
38 import com
.intellij
.openapi
.application
.ApplicationManager
;
39 import com
.intellij
.openapi
.components
.ServiceManager
;
40 import com
.intellij
.openapi
.editor
.Editor
;
41 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
42 import com
.intellij
.openapi
.project
.Project
;
43 import com
.intellij
.openapi
.ui
.popup
.JBPopup
;
44 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
45 import com
.intellij
.openapi
.util
.*;
46 import com
.intellij
.openapi
.wm
.*;
47 import com
.intellij
.openapi
.wm
.ex
.ToolWindowManagerEx
;
48 import com
.intellij
.openapi
.wm
.ex
.WindowManagerEx
;
49 import com
.intellij
.psi
.*;
50 import com
.intellij
.psi
.impl
.source
.tree
.injected
.InjectedLanguageUtil
;
51 import com
.intellij
.psi
.presentation
.java
.SymbolPresentationUtil
;
52 import com
.intellij
.psi
.util
.PsiTreeUtil
;
53 import com
.intellij
.psi
.util
.PsiUtilBase
;
54 import com
.intellij
.ui
.content
.*;
55 import com
.intellij
.ui
.popup
.AbstractPopup
;
56 import com
.intellij
.ui
.popup
.NotLookupOrSearchCondition
;
57 import com
.intellij
.ui
.popup
.PopupUpdateProcessor
;
58 import com
.intellij
.util
.Alarm
;
59 import com
.intellij
.util
.Processor
;
60 import com
.intellij
.util
.containers
.ContainerUtil
;
61 import com
.intellij
.util
.ui
.update
.Activatable
;
62 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
63 import org
.jetbrains
.annotations
.NonNls
;
64 import org
.jetbrains
.annotations
.NotNull
;
65 import org
.jetbrains
.annotations
.Nullable
;
69 import java
.awt
.event
.ActionEvent
;
70 import java
.awt
.event
.ActionListener
;
71 import java
.lang
.ref
.WeakReference
;
73 import java
.util
.List
;
75 public class DocumentationManager
{
76 private static final String SHOW_DOCUMENTATION_IN_TOOL_WINDOW
= "ShowDocumentationInToolWindow";
77 private static final String DOCUMENTATION_AUTO_UPDATE_ENABLED
= "DocumentationAutoUpdateEnabled";
78 @NonNls public static final String JAVADOC_LOCATION_AND_SIZE
= "javadoc.popup";
79 private final Project myProject
;
80 private Editor myEditor
= null;
81 private ParameterInfoController myParameterInfoController
;
82 private final Alarm myUpdateDocAlarm
;
83 private WeakReference
<JBPopup
> myDocInfoHintRef
;
84 private Component myPreviouslyFocused
= null;
85 public static final Key
<SmartPsiElementPointer
> ORIGINAL_ELEMENT_KEY
= Key
.create("Original element");
86 @NonNls public static final String PSI_ELEMENT_PROTOCOL
= "psi_element://";
87 @NonNls private static final String DOC_ELEMENT_PROTOCOL
= "doc_element://";
88 private ToolWindow myToolWindow
= null;
90 private final ActionManagerEx myActionManagerEx
;
92 private static final int ourFlagsForTargetElements
= TargetElementUtilBase
.getInstance().getAllAccepted();
93 private boolean myAutoUpdateDocumentation
= PropertiesComponent
.getInstance().isTrueValue(DOCUMENTATION_AUTO_UPDATE_ENABLED
);
94 private Runnable myAutoUpdateRequest
;
96 public static DocumentationManager
getInstance(Project project
) {
97 return ServiceManager
.getService(project
, DocumentationManager
.class);
100 public DocumentationManager(Project project
, ActionManagerEx managerEx
) {
102 myActionManagerEx
= managerEx
;
103 final AnActionListener actionListener
= new AnActionListener() {
104 public void beforeActionPerformed(AnAction action
, DataContext dataContext
, AnActionEvent event
) {
105 final JBPopup hint
= getDocInfoHint();
107 if (action
instanceof HintManagerImpl
.ActionToIgnore
) return;
108 if (action
== myActionManagerEx
.getAction(IdeActions
.ACTION_EDITOR_MOVE_CARET_DOWN
)) return;
109 if (action
== myActionManagerEx
.getAction(IdeActions
.ACTION_EDITOR_MOVE_CARET_UP
)) return;
110 if (action
== myActionManagerEx
.getAction(IdeActions
.ACTION_EDITOR_MOVE_CARET_PAGE_DOWN
)) return;
111 if (action
== myActionManagerEx
.getAction(IdeActions
.ACTION_EDITOR_MOVE_CARET_PAGE_UP
)) return;
112 if (action
== ActionManagerEx
.getInstanceEx().getAction(IdeActions
.ACTION_EDITOR_ESCAPE
)) return;
117 public void beforeEditorTyping(char c
, DataContext dataContext
) {
118 final JBPopup hint
= getDocInfoHint();
125 public void afterActionPerformed(final AnAction action
, final DataContext dataContext
, AnActionEvent event
) {
128 myActionManagerEx
.addAnActionListener(actionListener
, project
);
129 myUpdateDocAlarm
= new Alarm(Alarm
.ThreadToUse
.OWN_THREAD
,myProject
);
132 public void showJavaDocInfo(@NotNull final PsiElement element
, final PsiElement original
) {
133 PopupUpdateProcessor updateProcessor
= new PopupUpdateProcessor(element
.getProject()) {
134 public void updatePopup(Object lookupItemObject
) {
135 if (lookupItemObject
instanceof PsiElement
) {
136 doShowJavaDocInfo((PsiElement
)lookupItemObject
, true, false, this, original
);
141 doShowJavaDocInfo(element
, true, false, updateProcessor
, original
);
144 public void showJavaDocInfo(final Editor editor
, @Nullable final PsiFile file
, boolean requestFocus
) {
146 final Project project
= getProject(file
);
147 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
149 final PsiElement list
=
150 ParameterInfoController
.findArgumentList(file
, editor
.getCaretModel().getOffset(), -1);
152 myParameterInfoController
= ParameterInfoController
.findControllerAtOffset(editor
, list
.getTextRange().getStartOffset());
155 final PsiElement originalElement
= file
!= null ? file
.findElementAt(editor
.getCaretModel().getOffset()) : null;
156 PsiElement element
= findTargetElement(editor
, file
, originalElement
);
158 if (element
== null && myParameterInfoController
!= null) {
159 final Object
[] objects
= myParameterInfoController
.getSelectedElements();
161 if (objects
!= null && objects
.length
> 0) {
162 if (objects
[0] instanceof PsiElement
) {
163 element
= (PsiElement
)objects
[0];
168 if (element
== null && file
== null) return; //file == null for text field editor
170 if (element
== null) { // look if we are within a javadoc comment
171 element
= originalElement
;
172 if (element
== null) return;
173 PsiComment comment
= PsiTreeUtil
.getParentOfType(element
, PsiComment
.class);
174 if (comment
== null) return;
175 element
= comment
.getParent();
176 //if (!(element instanceof PsiDocCommentOwner)) return null;
179 storeOriginalElement(project
, originalElement
, element
);
181 final PopupUpdateProcessor updateProcessor
= new PopupUpdateProcessor(project
) {
182 public void updatePopup(Object lookupIteObject
) {
183 if (lookupIteObject
instanceof PsiElement
) {
184 doShowJavaDocInfo((PsiElement
)lookupIteObject
, false, false, this, originalElement
);
188 DocumentationProvider documentationProvider
= getProviderFromElement(file
);
190 PsiElement element
= null;
191 if (documentationProvider
!=null) {
192 element
= documentationProvider
.getDocumentationElementForLookupItem(
193 PsiManager
.getInstance(myProject
),
199 if (element
== null) return;
201 if (myEditor
!= null) {
202 final PsiFile file
= element
.getContainingFile();
204 Editor editor
= myEditor
;
205 showJavaDocInfo(myEditor
, file
, false);
210 doShowJavaDocInfo(element
, false, false, this, originalElement
);
215 doShowJavaDocInfo(element
, false, requestFocus
, updateProcessor
, originalElement
);
218 private void doShowJavaDocInfo(final PsiElement element
, boolean heavyWeight
, boolean requestFocus
, PopupUpdateProcessor updateProcessor
, final PsiElement originalElement
) {
219 Project project
= getProject(element
);
221 if (myToolWindow
== null && PropertiesComponent
.getInstance().isTrueValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW
)) {
222 createToolWindow(element
, originalElement
);
223 } else if (myToolWindow
!= null) {
224 final Content content
= myToolWindow
.getContentManager().getSelectedContent();
225 if (content
!= null) {
226 final DocumentationComponent component
= (DocumentationComponent
) content
.getComponent();
227 if (component
.getElement() != element
) {
228 content
.setDisplayName(getTitle(element
, true));
229 fetchDocInfo(getDefaultCollector(element
, originalElement
), component
, true);
232 if (!myToolWindow
.isVisible()) myToolWindow
.show(null);
235 final DocumentationComponent component
= new DocumentationComponent(this);
236 Processor
<JBPopup
> pinCallback
= new Processor
<JBPopup
>() {
237 public boolean process(JBPopup popup
) {
238 createToolWindow(element
, originalElement
);
244 final List
<Pair
<ActionListener
, KeyStroke
>> actions
= Collections
.singletonList(Pair
.<ActionListener
, KeyStroke
>create(new ActionListener() {
245 public void actionPerformed(ActionEvent e
) {
246 createToolWindow(element
, originalElement
);
247 final JBPopup hint
= getDocInfoHint();
248 if (hint
!= null && hint
.isVisible()) hint
.cancel();
250 }, ActionManagerEx
.getInstanceEx().getKeyboardShortcut("QuickJavaDoc").getFirstKeyStroke()));
252 final JBPopup hint
= JBPopupFactory
.getInstance().createComponentPopupBuilder(component
, component
)
253 .setRequestFocusCondition(project
, NotLookupOrSearchCondition
.INSTANCE
)
255 .addListener(updateProcessor
)
256 .addUserData(updateProcessor
)
257 .setKeyboardActions(actions
)
258 .setForceHeavyweight(heavyWeight
)
259 .setDimensionServiceKey(myProject
, JAVADOC_LOCATION_AND_SIZE
, false)
262 .setTitle(getTitle(element
, false))
263 .setCouldPin(pinCallback
)
264 .setCancelCallback(new Computable
<Boolean
>() {
265 public Boolean
compute() {
266 if (fromQuickSearch()) {
267 ((ChooseByNameBase
.JPanelProvider
)myPreviouslyFocused
.getParent()).unregisterHint();
270 Disposer
.dispose(component
);
272 myPreviouslyFocused
= null;
273 myParameterInfoController
= null;
280 AbstractPopup oldHint
= (AbstractPopup
)getDocInfoHint();
281 if (oldHint
!= null) {
282 DocumentationComponent oldComponent
= (DocumentationComponent
)oldHint
.getComponent();
283 PsiElement element1
= oldComponent
.getElement();
284 if (Comparing
.equal(element
, element1
)) {
286 component
.getComponent().requestFocus();
293 component
.setHint(hint
);
295 fetchDocInfo(getDefaultCollector(element
, originalElement
), component
);
297 myDocInfoHintRef
= new WeakReference
<JBPopup
>(hint
);
298 myPreviouslyFocused
= WindowManagerEx
.getInstanceEx().getFocusedComponent(project
);
300 if (fromQuickSearch()) {
301 ((ChooseByNameBase
.JPanelProvider
)myPreviouslyFocused
.getParent()).registerHint(hint
);
306 private void createToolWindow(final PsiElement element
, PsiElement originalElement
) {
307 assert myToolWindow
== null;
309 final DocumentationComponent component
= new DocumentationComponent(this, new AnAction
[]{
310 new ToggleAction("Auto show documentation for selected element", "Show documentation for current element automatically",
311 IconLoader
.getIcon("/general/autoscrollFromSource.png")) {
313 public boolean isSelected(AnActionEvent e
) {
314 return myAutoUpdateDocumentation
;
318 public void setSelected(AnActionEvent e
, boolean state
) {
319 PropertiesComponent
.getInstance().setValue(DOCUMENTATION_AUTO_UPDATE_ENABLED
, Boolean
.TRUE
.toString());
320 myAutoUpdateDocumentation
= state
;
321 restartAutoUpdate(state
);
324 new AnAction("Restore popup behavior", "Restore documentation popup behavior", IconLoader
.getIcon("/actions/cancel.png")) {
326 public void actionPerformed(AnActionEvent e
) {
327 restorePopupBehavior();
331 final ToolWindowManagerEx toolWindowManagerEx
= ToolWindowManagerEx
.getInstanceEx(myProject
);
332 myToolWindow
= toolWindowManagerEx
.registerToolWindow(ToolWindowId
.DOCUMENTATION
, true, ToolWindowAnchor
.RIGHT
, myProject
);
333 myToolWindow
.setIcon(IconLoader
.getIcon("/general/documentation.png"));
335 myToolWindow
.setAvailable(true, null);
336 myToolWindow
.setToHideOnEmptyContent(false);
337 myToolWindow
.setAutoHide(false);
339 final Rectangle rectangle
= WindowManager
.getInstance().getIdeFrame(myProject
).suggestChildFrameBounds();
340 myToolWindow
.setDefaultState(ToolWindowAnchor
.RIGHT
, ToolWindowType
.FLOATING
, rectangle
);
342 final ContentManager contentManager
= myToolWindow
.getContentManager();
343 final ContentFactory contentFactory
= ContentFactory
.SERVICE
.getInstance();
344 final Content content
= contentFactory
.createContent(component
, getTitle(element
, true), false);
345 contentManager
.addContent(content
);
347 contentManager
.addContentManagerListener(new ContentManagerAdapter() {
349 public void contentRemoved(ContentManagerEvent event
) {
350 if (contentManager
.getContentCount() == 0) {
351 restorePopupBehavior();
356 new UiNotifyConnector(component
, new Activatable() {
357 public void showNotify() {
358 restartAutoUpdate(myAutoUpdateDocumentation
);
361 public void hideNotify() {
362 restartAutoUpdate(false);
366 myToolWindow
.show(null);
367 PropertiesComponent
.getInstance().setValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW
, Boolean
.TRUE
.toString());
368 restartAutoUpdate(PropertiesComponent
.getInstance().isTrueValue(DOCUMENTATION_AUTO_UPDATE_ENABLED
));
369 fetchDocInfo(getDefaultCollector(element
, originalElement
), component
);
372 private void restartAutoUpdate(final boolean state
) {
373 if (state
&& myToolWindow
!= null) {
374 if (myAutoUpdateRequest
== null) {
375 myAutoUpdateRequest
= new Runnable() {
377 final DataContext dataContext
= DataManager
.getInstance().getDataContext();
378 final Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
379 if (editor
!= null) {
380 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(editor
, myProject
);
382 final Editor injectedEditor
= InjectedLanguageUtil
.getEditorForInjectedLanguageNoCommit(editor
, file
);
383 if (injectedEditor
!= null) {
384 final PsiFile psiFile
= PsiUtilBase
.getPsiFileInEditor(injectedEditor
, myProject
);
385 if (psiFile
!= null) {
386 showJavaDocInfo(injectedEditor
, psiFile
, false);
392 showJavaDocInfo(editor
, file
, false);
398 IdeEventQueue
.getInstance().addIdleListener(myAutoUpdateRequest
, 500);
401 if (myAutoUpdateRequest
!= null) {
402 IdeEventQueue
.getInstance().removeIdleListener(myAutoUpdateRequest
);
403 myAutoUpdateRequest
= null;
408 private void restorePopupBehavior() {
409 if (myToolWindow
!= null) {
410 PropertiesComponent
.getInstance().setValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW
, Boolean
.FALSE
.toString());
411 ToolWindowManagerEx
.getInstanceEx(myProject
).unregisterToolWindow(ToolWindowId
.DOCUMENTATION
);
413 restartAutoUpdate(false);
417 private static String
getTitle(@NotNull final PsiElement element
, final boolean _short
) {
418 final String title
= SymbolPresentationUtil
.getSymbolPresentableText(element
);
419 return _short ? title
!= null ? title
: element
.getText() : CodeInsightBundle
.message("javadoc.info.title", title
!= null ? title
: element
.getText());
422 public static void storeOriginalElement(final Project project
, final PsiElement originalElement
, final PsiElement element
) {
423 if (element
== null) return;
426 ORIGINAL_ELEMENT_KEY
,
427 SmartPointerManager
.getInstance(project
).createSmartPsiElementPointer(originalElement
)
429 } catch (RuntimeException ex
) {
430 // PsiPackage does not allow putUserData
435 public PsiElement
findTargetElement(final Editor editor
, @Nullable final PsiFile file
, PsiElement contextElement
) {
436 PsiElement element
= editor
!= null ? TargetElementUtilBase
.findTargetElement(editor
, ourFlagsForTargetElements
) : null;
438 // Allow context doc over xml tag content
439 if (element
!= null || contextElement
!= null) {
440 final PsiElement adjusted
= TargetElementUtilBase
.getInstance()
441 .adjustElement(editor
, ourFlagsForTargetElements
, element
, contextElement
);
442 if (adjusted
!= null) {
447 if (element
== null && editor
!= null) {
448 element
= getElementFromLookup(editor
, file
);
450 if (element
== null) {
451 final PsiReference ref
= TargetElementUtilBase
.findReference(editor
, editor
.getCaretModel().getOffset());
454 element
= TargetElementUtilBase
.getInstance().adjustReference(ref
);
455 if (element
== null && ref
instanceof PsiPolyVariantReference
) {
456 element
= ref
.getElement();
462 storeOriginalElement(myProject
, contextElement
, element
);
468 public PsiElement
getElementFromLookup(final Editor editor
, @Nullable final PsiFile file
) {
470 final Lookup activeLookup
= LookupManager
.getInstance(myProject
).getActiveLookup();
472 if (activeLookup
!= null) {
473 LookupElement item
= activeLookup
.getCurrentItem();
476 final PsiElement contextElement
= file
!= null ? file
.findElementAt(editor
.getCaretModel().getOffset()) : null;
477 final PsiReference ref
= TargetElementUtilBase
.findReference(editor
, editor
.getCaretModel().getOffset());
479 final DocumentationProvider documentationProvider
= getProviderFromElement(file
);
481 return documentationProvider
.getDocumentationElementForLookupItem(
482 PsiManager
.getInstance(myProject
),
484 ref
!= null ? ref
.getElement():contextElement
491 private boolean fromQuickSearch() {
492 return myPreviouslyFocused
!= null && myPreviouslyFocused
.getParent() instanceof ChooseByNameBase
.JPanelProvider
;
495 private DocumentationCollector
getDefaultCollector(@NotNull final PsiElement element
, @Nullable final PsiElement originalElement
) {
496 return new DocumentationCollector() {
499 public String
getDocumentation() throws Exception
{
500 final DocumentationProvider provider
= getProviderFromElement(element
, originalElement
);
501 if (myParameterInfoController
!= null) {
502 final Object
[] objects
= myParameterInfoController
.getSelectedElements();
504 if (objects
.length
> 0) {
505 @NonNls StringBuffer sb
= null;
507 for(Object o
:objects
) {
508 PsiElement parameter
= null;
509 if (o
instanceof PsiElement
) {
510 parameter
= (PsiElement
)o
;
513 if (parameter
!= null) {
514 final SmartPsiElementPointer originalElement
= parameter
.getUserData(ORIGINAL_ELEMENT_KEY
);
515 final String str2
= provider
.generateDoc(parameter
, originalElement
!= null ? originalElement
.getElement() : null);
516 if (str2
== null) continue;
517 if (sb
== null) sb
= new StringBuffer();
526 if (sb
!= null) return sb
.toString();
530 final SmartPsiElementPointer originalElement
= element
.getUserData(ORIGINAL_ELEMENT_KEY
);
531 return provider
.generateDoc(element
, originalElement
!= null ? originalElement
.getElement() : null);
535 public PsiElement
getElement() {
536 return element
.isValid() ? element
: null;
542 public JBPopup
getDocInfoHint() {
543 if (myDocInfoHintRef
== null) return null;
544 JBPopup hint
= myDocInfoHintRef
.get();
545 if (hint
== null || !hint
.isVisible()) {
546 myDocInfoHintRef
= null;
552 public void fetchDocInfo(final DocumentationCollector provider
, final DocumentationComponent component
) {
553 doFetchDocInfo(component
, provider
, true, false);
556 public void fetchDocInfo(final DocumentationCollector provider
, final DocumentationComponent component
, final boolean clearHistory
) {
557 doFetchDocInfo(component
, provider
, true, clearHistory
);
560 public void fetchDocInfo(final PsiElement element
, final DocumentationComponent component
) {
561 doFetchDocInfo(component
, getDefaultCollector(element
, null), true, false);
564 public ActionCallback
queueFetchDocInfo(final DocumentationCollector provider
, final DocumentationComponent component
, final boolean clearHistory
) {
565 return doFetchDocInfo(component
, provider
, false, clearHistory
);
568 public ActionCallback
queueFetchDocInfo(final PsiElement element
, final DocumentationComponent component
) {
569 return queueFetchDocInfo(getDefaultCollector(element
, null), component
, false);
572 private ActionCallback
doFetchDocInfo(final DocumentationComponent component
, final DocumentationCollector provider
, final boolean cancelRequests
, final boolean clearHistory
) {
573 final ActionCallback callback
= new ActionCallback();
574 component
.startWait();
575 if (cancelRequests
) {
576 myUpdateDocAlarm
.cancelAllRequests();
578 if (component
.isEmpty()) {
579 component
.setText(CodeInsightBundle
.message("javadoc.fetching.progress"), null, clearHistory
);
582 myUpdateDocAlarm
.addRequest(new Runnable() {
584 final Throwable
[] ex
= new Throwable
[1];
585 final String text
= ApplicationManager
.getApplication().runReadAction(new Computable
<String
>() {
587 public String
compute() {
589 return provider
.getDocumentation();
591 catch (Throwable e
) {
598 //noinspection SSBasedInspection
599 SwingUtilities
.invokeLater(new Runnable() {
601 String message
= ex
[0] instanceof IndexNotReadyException
602 ?
"Documentation is not available until indices are built."
603 : CodeInsightBundle
.message("javadoc.external.fetch.error.message", ex
[0].getLocalizedMessage());
604 component
.setText(message
, null, true);
611 final PsiElement element
= ApplicationManager
.getApplication().runReadAction(new Computable
<PsiElement
>() {
613 public PsiElement
compute() {
614 return provider
.getElement();
617 if (element
== null) {
620 //noinspection SSBasedInspection
621 SwingUtilities
.invokeLater(new Runnable() {
623 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
625 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
629 if (!element
.isValid()) {
635 component
.setText(CodeInsightBundle
.message("no.documentation.found"), element
, true);
637 else if (text
.length() == 0) {
638 component
.setText(component
.getText(), element
, true, clearHistory
);
641 component
.setData(element
, text
, clearHistory
);
644 final AbstractPopup jbPopup
= (AbstractPopup
)getDocInfoHint();
649 jbPopup
.setCaption(getTitle(element
, false));
650 final String dimensionServiceKey
= jbPopup
.getDimensionServiceKey();
651 Dimension dimension
= component
.getPreferredSize();
652 final Dimension storedSize
= dimensionServiceKey
!= null ? DimensionService
.getInstance().getSize(dimensionServiceKey
, getProject(element
)) : null;
653 if (storedSize
!= null) {
654 dimension
= storedSize
;
656 final Window window
= SwingUtilities
.getWindowAncestor(component
);
657 if (window
!= null) {
658 window
.setBounds(window
.getX(), window
.getY(), dimension
.width
, dimension
.height
);
671 public static DocumentationProvider
getProviderFromElement(final PsiElement element
) {
672 return getProviderFromElement(element
, null);
676 private static DocumentationProvider
getProviderFromElement(@Nullable PsiElement element
, @Nullable PsiElement originalElement
) {
677 if (element
!= null && !element
.isValid()) {
680 if (originalElement
!= null && !originalElement
.isValid()) {
681 originalElement
= null;
684 if (originalElement
== null) {
685 originalElement
= getOriginalElement(element
);
688 PsiFile containingFile
=
689 originalElement
!= null ? originalElement
.getContainingFile() : element
!= null ? element
.getContainingFile() : null;
690 Set
<DocumentationProvider
> result
= new LinkedHashSet
<DocumentationProvider
>();
692 final Language containingFileLanguage
= containingFile
!= null ? containingFile
.getLanguage() : null;
693 DocumentationProvider originalProvider
=
694 containingFile
!= null ? LanguageDocumentation
.INSTANCE
.forLanguage(containingFileLanguage
) : null;
696 final Language elementLanguage
= element
!= null ? element
.getLanguage() : null;
697 DocumentationProvider elementProvider
=
698 element
== null || elementLanguage
.is(containingFileLanguage
) ?
null : LanguageDocumentation
.INSTANCE
.forLanguage(elementLanguage
);
700 addProviderToResult(result
, elementProvider
);
701 addProviderToResult(result
, originalProvider
);
703 if (containingFile
!= null) {
704 final Language baseLanguage
= containingFile
.getViewProvider().getBaseLanguage();
705 if (!baseLanguage
.is(containingFileLanguage
)) {
706 addProviderToResult(result
, LanguageDocumentation
.INSTANCE
.forLanguage(baseLanguage
));
709 // return extensible documentation provider even if the list is empty
710 return new CompositeDocumentationProvider(result
);
713 private static void addProviderToResult(final Set
<DocumentationProvider
> result
, final DocumentationProvider t
) {
714 if (t
instanceof CompositeDocumentationProvider
) result
.addAll(((CompositeDocumentationProvider
)t
).getProviders());
715 else ContainerUtil
.addIfNotNull(t
, result
);
719 public static PsiElement
getOriginalElement(final PsiElement element
) {
720 SmartPsiElementPointer originalElementPointer
= element
!=null ? element
.getUserData(ORIGINAL_ELEMENT_KEY
):null;
721 return originalElementPointer
!= null ? originalElementPointer
.getElement() : null;
724 void navigateByLink(final DocumentationComponent component
, String url
) {
725 component
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
726 final PsiElement psiElement
= component
.getElement();
727 final PsiManager manager
= PsiManager
.getInstance(getProject(psiElement
));
728 if (url
.startsWith(PSI_ELEMENT_PROTOCOL
)) {
729 final String refText
= url
.substring(PSI_ELEMENT_PROTOCOL
.length());
730 DocumentationProvider provider
= getProviderFromElement(psiElement
);
731 final PsiElement targetElement
= provider
.getDocumentationElementForLink(manager
, refText
, psiElement
);
732 if (targetElement
!= null) {
733 fetchDocInfo(getDefaultCollector(targetElement
, null), component
);
737 final String docUrl
= url
;
740 (new DocumentationCollector() {
741 public String
getDocumentation() throws Exception
{
742 if (docUrl
.startsWith(DOC_ELEMENT_PROTOCOL
)) {
743 final DocumentationProvider provider
= getProviderFromElement(psiElement
);
744 final List
<String
> urls
= provider
.getUrlFor(psiElement
, getOriginalElement(psiElement
));
745 BrowserUtil
.launchBrowser(urls
!= null && !urls
.isEmpty() ? urls
.get(0) : docUrl
);
747 BrowserUtil
.launchBrowser(docUrl
);
752 public PsiElement
getElement() {
753 //String loc = getElementLocator(docUrl);
756 // PsiElement context = component.getElement();
757 // return JavaDocUtil.findReferenceTarget(context.getManager(), loc, context);
765 component
.setCursor(Cursor
.getPredefinedCursor(Cursor
.DEFAULT_CURSOR
));
768 void showHint(final JBPopup hint
) {
769 if (myEditor
!= null) {
770 hint
.showInBestPositionFor(myEditor
);
772 else if (myPreviouslyFocused
!= null) {
773 hint
.showInBestPositionFor(DataManager
.getInstance().getDataContext(myPreviouslyFocused
));
775 hint
.showInBestPositionFor(DataManager
.getInstance().getDataContext());
779 public void requestFocus() {
780 if (fromQuickSearch()) {
781 myPreviouslyFocused
.getParent().requestFocus();
785 public Project
getProject(@Nullable final PsiElement element
) {
786 assert element
== null || !element
.isValid() || myProject
== element
.getProject();
790 @SuppressWarnings({"HardCodedStringLiteral"})
791 public static void createHyperlink(StringBuilder buffer
, String refText
,String label
,boolean plainLink
) {
792 buffer
.append("<a href=\"");
793 buffer
.append("psi_element://"); // :-)
794 buffer
.append(refText
);
795 buffer
.append("\">");
797 buffer
.append("<code>");
799 buffer
.append(label
);
801 buffer
.append("</code>");
803 buffer
.append("</a>");
806 private static interface DocumentationCollector
{
808 String
getDocumentation() throws Exception
;
810 PsiElement
getElement();