eclipse的JDT的hover是如何显示出来的

1.什么是JDT的hover。

见下图:

2. hover中的不同的字体,和颜色是如何显示出来的。

其实在对应的hover中,返回的是String类型,内容是html,然后通过org.eclipse.swt.browser.Browser显示出来的。

3.工作原理:

3.1 add MouseTracker to current SourceViewer, when you open java file.
org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor$AdaptedSourceViewer
-->
org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor$AdaptedSourceViewer

/**
 * After this method has been executed the caller knows that any installed text hover has been installed.
 */
private void ensureHoverControlManagerInstalled() {
    if (fTextHovers != null && !fTextHovers.isEmpty() && fHoverControlCreator != null && fTextHoverManager == null) {
        fTextHoverManager= new TextViewerHoverManager(this, fHoverControlCreator);
        fTextHoverManager.install(this.getTextWidget());
        fTextHoverManager.setSizeConstraints(TEXT_HOVER_WIDTH_CHARS, TEXT_HOVER_HEIGHT_CHARS, false, true);
        fTextHoverManager.setInformationControlReplacer(new StickyHoverManager(this));
    }
}

--> org.eclipse.jface.text.TextViewerHoverManager

public void install(Control subjectControl) {
    if (fSubjectControl != null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener != null)
        fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener);

    fSubjectControl= subjectControl;

    if (fSubjectControl != null)
        fSubjectControl.addDisposeListener(getSubjectControlDisposeListener());

    if (fInformationControlCloser != null)
        fInformationControlCloser.setSubjectControl(subjectControl);

    setEnabled(true);
    fDisposed= false;
}

--> org.eclipse.jface.text.TextViewerHoverManager

public void setEnabled(boolean enabled) {

    boolean was= isEnabled();
    super.setEnabled(enabled);
    boolean is= isEnabled();

    if (was != is && fMouseTracker != null) {
        if (is)
            fMouseTracker.start(getSubjectControl());
        else
            fMouseTracker.stop();
    }
}

-->
org.eclipse.jface.text.AbstractHoverInformationControlManager$MouseTracker

public void start(Control subjectControl) {
    fSubjectControl= subjectControl;
    if (fSubjectControl != null && !fSubjectControl.isDisposed())
        fSubjectControl.addMouseTrackListener(this);

    fIsInRestartMode= false;
    fIsComputing= false;
    fMouseLostWhileComputing= false;
    fShellDeactivatedWhileComputing= false;
}

3.2 when you hover mouse on target SourceViewer, it will show hover
org.eclipse.jface.text.AbstractHoverInformationControlManager$MouseTracker

public void mouseHover(MouseEvent event) {
    if (fIsComputing || fIsInRestartMode ||
            (fSubjectControl != null && !fSubjectControl.isDisposed() && fSubjectControl.getShell() != fSubjectControl.getShell().getDisplay().getActiveShell())) {
        if (DEBUG)
            System.out.println("AbstractHoverInformationControlManager...mouseHover: @ " + event.x + "/" + event.y + " : hover cancelled: fIsComputing= " + fIsComputing + ", fIsInRestartMode= " + fIsInRestartMode); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
        return;
    }

    fIsInRestartMode= true;
    fIsComputing= true;
    fMouseLostWhileComputing= false;
    fShellDeactivatedWhileComputing= false;

    fHoverEventStateMask= event.stateMask;
    fHoverEvent= event;
    fHoverArea= new Rectangle(event.x - EPSILON, event.y - EPSILON, 2 * EPSILON, 2 * EPSILON );
    if (fHoverArea.x < 0)
        fHoverArea.x= 0;
    if (fHoverArea.y < 0)
        fHoverArea.y= 0;
    setSubjectArea(fHoverArea);

    if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
        fSubjectControl.addMouseMoveListener(this);
        fSubjectControl.getShell().addShellListener(this);
    }
    doShowInformation();
}

--> org.eclipse.jface.text.AbstractInformationControlManager

protected void doShowInformation() {
    fSubjectArea= null;
    fInformation= null;
    computeInformation();
}

--> org.eclipse.jface.text.TextViewerHoverManager

protected void computeInformation() {
//create a thread(Text Viewer Hover Presenter) to show hover
}


-->
org.eclipse.jface.text.AbstractInformationControlManager.internalShowInformationControl(Rectangle,
Object) to create control to show the text.

private void internalShowInformationControl(Rectangle subjectArea, Object information) {
    if (this instanceof InformationControlReplacer) {
        ((InformationControlReplacer) this).showInformationControl(subjectArea, information);
        return;
    }

    IInformationControl informationControl= getInformationControl();
    if (informationControl != null) {

        Point sizeConstraints= computeSizeConstraints(fSubjectControl, fSubjectArea, informationControl);
        if (informationControl instanceof IInformationControlExtension3) {
            IInformationControlExtension3 iControl3= (IInformationControlExtension3) informationControl;
            Rectangle trim= iControl3.computeTrim();
            sizeConstraints.x += trim.width;
            sizeConstraints.y += trim.height;
        }
        informationControl.setSizeConstraints(sizeConstraints.x, sizeConstraints.y);

        if (informationControl instanceof IInformationControlExtension2)
            ((IInformationControlExtension2)informationControl).setInput(information);
        else
            informationControl.setInformation(information.toString());

        if (informationControl instanceof IInformationControlExtension) {
            IInformationControlExtension extension= (IInformationControlExtension)informationControl;
            if (!extension.hasContents())
                return;
        }

        Point size= null;
        Point location= null;
        Rectangle bounds= restoreInformationControlBounds();

        if (bounds != null) {
            if (bounds.x > -1 && bounds.y > -1)
                location= Geometry.getLocation(bounds);

            if (bounds.width > -1 && bounds.height > -1)
                size= Geometry.getSize(bounds);
        }

        if (size == null)
            size= informationControl.computeSizeHint();

        if (fEnforceAsMinimalSize)
            size= Geometry.max(size, sizeConstraints);
        if (fEnforceAsMaximalSize)
            size= Geometry.min(size, sizeConstraints);

        if (location == null)
            location= computeInformationControlLocation(subjectArea, size);

        Rectangle controlBounds= Geometry.createRectangle(location, size);
        cropToClosestMonitor(controlBounds);
        location= Geometry.getLocation(controlBounds);
        size= Geometry.getSize(controlBounds);
        informationControl.setLocation(location);
        informationControl.setSize(size.x, size.y);

        showInformationControl(subjectArea);
    }
}

--> org.eclipse.jface.text.AbstractInformationControlManager

protected void showInformationControl(Rectangle subjectArea) {
    fInformationControl.setVisible(true);

    if (fInformationControl == null)
        return; // could already be disposed if setVisible(..) runs the display loop

    if (fTakesFocusWhenVisible)
        fInformationControl.setFocus();

    if (fInformationControlCloser != null)
        fInformationControlCloser.start(subjectArea);
}

org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover$HoverControlCreator

public IInformationControl doCreateInformationControl(Shell parent) {
        String tooltipAffordanceString= fAdditionalInfoAffordance ? JavaPlugin.getAdditionalInfoAffordanceString() : EditorsUI.getTooltipAffordanceString();
        if (BrowserInformationControl.isAvailable(parent)) {
            String font= PreferenceConstants.APPEARANCE_JAVADOC_FONT;
            BrowserInformationControl iControl= new BrowserInformationControl(parent, font, tooltipAffordanceString) {
                /*
                 * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
                 */
                @Override
                public IInformationControlCreator getInformationPresenterControlCreator() {
                    return fInformationPresenterControlCreator;
                }
            };
            addLinkListener(iControl);
            return iControl;
        } else {
            return new DefaultInformationControl(parent, tooltipAffordanceString);
        }
    }

4. 下面是System.out.println的html内容和在firefox下显示的效果。

<html><head><style CHARSET="ISO-8859-1" TYPE="text/css">/* Font definitions */
html         { font-family: 'Sans',sans-serif; font-size: 10pt; font-style: normal; font-weight: normal; }
body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt { font-size: 1em; }
pre          { font-family: monospace; }

/* Margins */
body         { overflow: auto; margin-top: 0px; margin-bottom: 0.5em; margin-left: 0.3em; margin-right: 0px; }
h1           { margin-top: 0.3em; margin-bottom: 0.04em; }    
h2           { margin-top: 2em; margin-bottom: 0.25em; }
h3           { margin-top: 1.7em; margin-bottom: 0.25em; }
h4           { margin-top: 2em; margin-bottom: 0.3em; }
h5           { margin-top: 0px; margin-bottom: 0px; }
p            { margin-top: 1em; margin-bottom: 1em; }
pre          { margin-left: 0.6em; }
ul             { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em;}
li             { margin-top: 0px; margin-bottom: 0px; } 
li p         { margin-top: 0px; margin-bottom: 0px; } 
ol             { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em; }
dl             { margin-top: 0px; margin-bottom: 1em; }
dt             { margin-top: 0px; margin-bottom: 0px; font-weight: bold; }
dd             { margin-top: 0px; margin-bottom: 0px; }

/* Styles and colors */
a:link         { color: #0000FF; }
a:hover         { color: #000080; }
a:visited    { text-decoration: underline; }
a.header:link    { text-decoration: none; color: #000000 }
a.header:visited { text-decoration: none; color: #000000 }
a.header:hover   { text-decoration: underline; color: #000080; }
h4           { font-style: italic; }
strong         { font-weight: bold; }
em             { font-style: italic; }
var             { font-style: italic; }
th             { font-weight: bold; }

/* Workarounds for new Javadoc stylesheet (1.7) */ 
ul.blockList li.blockList, ul.blockListLast li.blockList {
    list-style:none;
}
ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
    list-style:none;
}
</style>
<base href='http://download.oracle.com/javase/7/docs/api/java/io/PrintStream.html'>
</head><body style="overflow:hidden;" text="#000000" bgcolor="#ffffbf"><h5><div style='word-wrap: break-word; position: relative; margin-left: 20px; padding-top: 2px; '><a href=''><!--[if lte IE 6]><![if gte IE 5.5]>
<span alt='Open Declaration' style="border:none; position: absolute; width: 16px; height: 16px; left: -21px; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='')"></span>
<![endif]><![endif]-->
<!--[if !IE]>-->
<img alt='Open Declaration' style='border:none; position: absolute; width: 16px; height: 16px; left: -21px; ' src='/>
<!--<![endif]-->
<!--[if gte IE 7]>
<img alt='Open Declaration' style='border:none; position: absolute; width: 16px; height: 16px; left: -21px; ' src='file:'/>
<![endif]-->
</a>void <a class='header' href=''>java</a>.<a class='header' href=''>io</a>.<a class='header' href=''>PrintStream</a>.println(<span style='font-weight:normal;'></span>int x)</div></h5><br><p>Prints an integer and then terminate the line.  This method behaves as
 though it invokes <code><code><a href=''>print(int)</a></code></code> and then
 <code><code><a href=''>println()</a></code></code>.<dl><dt>Parameters:</dt><dd><b>x</b>   The <code>int</code> to be printed.</dd></dl></body></html>

显示效果: