diff --git a/core/org.eclipse.cdt.ui/ChangeLog b/core/org.eclipse.cdt.ui/ChangeLog index 4856529eef0..2efed43a55b 100644 --- a/core/org.eclipse.cdt.ui/ChangeLog +++ b/core/org.eclipse.cdt.ui/ChangeLog @@ -1,3 +1,17 @@ +2002-12-06 David Inglis + + * src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java: + * srcsrc/org/eclipse/cdt/internal/ui/text/CAnnotationHover.java: + * src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java: + * src/org/eclipse/cdt/internal/ui/text/HTML2TextReader.java: New + * src/org/eclipse/cdt/internal/ui/text/HTMLPrinter.java: New + * src/org/eclipse/cdt/internal/ui/text/HTMLTextPresenter.java: New + * src/org/eclipse/cdt/internal/ui/text/LineBreakingReader.java + * src/org/eclipse/cdt/internal/ui/text/SubstitutionTextReader.java: New + + Added support to display balloon messages for lines with multiple markers. + Added support for basic markup within the hover balloons within the CEditor. + 2002-12-04 Alex Chapiro I propose to create isValidLocation method in addition to already existing diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java index e029e62290a..d1de7429e4a 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java @@ -23,6 +23,7 @@ import org.eclipse.cdt.core.index.IndexModel; import org.eclipse.cdt.core.index.TagFlags; import org.eclipse.cdt.internal.ui.CCompletionContributorManager; import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.cdt.internal.ui.text.HTMLPrinter; import org.eclipse.cdt.ui.IFunctionSummary; public class DefaultCEditorTextHover implements ITextHover @@ -42,7 +43,6 @@ public class DefaultCEditorTextHover implements ITextHover */ public String getHoverInfo( ITextViewer viewer, IRegion region ) { - String result = null; String expression = null; if(fEditor == null) @@ -54,26 +54,23 @@ public class DefaultCEditorTextHover implements ITextHover if ( expression.length() == 0 ) return null; + StringBuffer buffer = new StringBuffer(); + // We are just doing some C, call the Help to get info IFunctionSummary fs = CCompletionContributorManager.getFunctionInfo(expression); if(fs != null) { - StringBuffer s = new StringBuffer(); - s.append(expression + "() - " + fs.getSummary() + "\n\n" + fs.getSynopsis()); + buffer.append("" + HTMLPrinter.convertToHTMLContent(expression) + + "() - " + HTMLPrinter.convertToHTMLContent(fs.getSummary()) + + "

" + HTMLPrinter.convertToHTMLContent(fs.getSynopsis())); int i; - for(i = 0; i < s.length(); i++) { - if(s.charAt(i) == '\\') { - if((i + 1 < s.length()) && s.charAt(i+1) == 'n') { - s.replace(i, i + 2, "\n"); + for(i = 0; i < buffer.length(); i++) { + if(buffer.charAt(i) == '\\') { + if((i + 1 < buffer.length()) && buffer.charAt(i+1) == 'n') { + buffer.replace(i, i + 2, "
"); } } } - i = s.length(); - // Eat the last cariage return for nicer looking text - if(i != 0 && s.charAt(i - 1) == '\n') { - s.replace(i - 1, i, ""); - } - return s.toString(); } else { // Query the C model IndexModel model = IndexModel.getDefault(); @@ -102,14 +99,18 @@ public class DefaultCEditorTextHover implements ITextHover if(tags != null && tags.length > 0) { ITagEntry selectedTag = selectTag(tags); // Show only the first element - StringBuffer s = new StringBuffer(); - s.append(expression + "() - " + selectedTag.getIFile().getFullPath().toString() + "[" + selectedTag.getLineNumber()+"]" ); + buffer.append("" + HTMLPrinter.convertToHTMLContent(expression) + + "() - " + selectedTag.getIFile().getFullPath().toString() + "[" + selectedTag.getLineNumber()+"]" ); // Now add the pattern - s.append("\n\n" + selectedTag.getPattern()); - return s.toString(); + buffer.append("

" + HTMLPrinter.convertToHTMLContent(selectedTag.getPattern())); } } } + if (buffer.length() > 0) { + HTMLPrinter.insertPageProlog(buffer, 0); + HTMLPrinter.addPageEpilog(buffer); + return buffer.toString(); + } } catch( BadLocationException x ) { @@ -119,8 +120,6 @@ public class DefaultCEditorTextHover implements ITextHover { // ignore } - if ( expression != null && result != null ) - return expression + " = " + result; return null; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAnnotationHover.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAnnotationHover.java index 5ef328117df..3f52ee21437 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAnnotationHover.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAnnotationHover.java @@ -5,37 +5,21 @@ package org.eclipse.cdt.internal.ui.text; * All Rights Reserved. */ -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; - -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Display; - - import org.eclipse.core.resources.IMarker; - import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; - - +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; import org.eclipse.ui.texteditor.MarkerAnnotation; - -import org.eclipse.cdt.ui.CUIPlugin; - - - - public class CAnnotationHover implements IAnnotationHover { /** @@ -58,20 +42,17 @@ public class CAnnotationHover implements IAnnotationHover { } /** - * Selects one marker from the two lists. + * Selects a set of markers from the two lists. By default, it just returns + * the set of exact matches. */ - protected IMarker select(List firstChoice, List secondChoice) { - if (!firstChoice.isEmpty()) - return (IMarker) firstChoice.get(0); - if (!secondChoice.isEmpty()) - return (IMarker) secondChoice.get(0); - return null; + protected List select(List exactMatch, List including) { + return exactMatch; } - + /** * Returns one marker which includes the ruler's line of activity. */ - protected IMarker getMarker(ISourceViewer viewer, int line) { + protected List getMarkersForLine(ISourceViewer viewer, int line) { IDocument document= viewer.getDocument(); IAnnotationModel model= viewer.getAnnotationModel(); @@ -101,50 +82,44 @@ public class CAnnotationHover implements IAnnotationHover { return select(exact, including); } - - - /** + /* * @see IVerticalRulerHover#getHoverInfo(ISourceViewer, int) */ public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) { - IMarker marker= getMarker(sourceViewer, lineNumber); - if (marker != null) { - String text= marker.getAttribute(IMarker.MESSAGE, (String) null); - if (text != null) { - return formatHoverText(text, sourceViewer.getTextWidget().getDisplay()); - } - } - return null; - } - - /* - * Formats the message of this hover to fit onto the screen. - */ - private String formatHoverText(String text, Display display) { - String lineDelim= System.getProperty("line.separator", "\n"); - - Reader textReader= new StringReader(text); - GC gc= new GC(display); - try { - StringBuffer buf= new StringBuffer(); + List markers= getMarkersForLine(sourceViewer, lineNumber); + if (markers != null) { - LineBreakingReader reader= new LineBreakingReader(textReader, gc, getHoverWidth(display)); - String line= reader.readLine(); - while (line != null) { - if (buf.length() != 0) { - buf.append(lineDelim); + if (markers.size() == 1) { + + // optimization + IMarker marker= (IMarker) markers.get(0); + String message= marker.getAttribute(IMarker.MESSAGE, (String) null); + if (message != null && message.trim().length() > 0) + return formatSingleMessage(message); + + } else { + + List messages= new ArrayList(); + + Iterator e= markers.iterator(); + while (e.hasNext()) { + IMarker marker= (IMarker) e.next(); + String message= marker.getAttribute(IMarker.MESSAGE, (String) null); + if (message != null && message.trim().length() > 0) + messages.add(message.trim()); } - buf.append(line); - line= reader.readLine(); + + if (messages.size() == 1) + return formatSingleMessage((String) messages.get(0)); + + if (messages.size() > 1) + return formatMultipleMessages(messages); } - return buf.toString(); - } catch (IOException e) { - CUIPlugin.log(e); - } finally { - gc.dispose(); } + return null; } + private int getHoverWidth(Display display) { Rectangle displayBounds= display.getBounds(); @@ -156,5 +131,32 @@ public class CAnnotationHover implements IAnnotationHover { return hoverWidth; } + /* + * Formats a message as HTML text. + */ + private String formatSingleMessage(String message) { + StringBuffer buffer= new StringBuffer(); + HTMLPrinter.addPageProlog(buffer); + HTMLPrinter.addParagraph(buffer, HTMLPrinter.convertToHTMLContent(message)); + HTMLPrinter.addPageEpilog(buffer); + return buffer.toString(); + } + /* + * Formats several message as HTML text. + */ + private String formatMultipleMessages(List messages) { + StringBuffer buffer= new StringBuffer(); + HTMLPrinter.addPageProlog(buffer); + HTMLPrinter.addParagraph(buffer, HTMLPrinter.convertToHTMLContent("Multiple markers at this line")); + + HTMLPrinter.startBulletList(buffer); + Iterator e= messages.iterator(); + while (e.hasNext()) + HTMLPrinter.addBullet(buffer, HTMLPrinter.convertToHTMLContent((String) e.next())); + HTMLPrinter.endBulletList(buffer); + + HTMLPrinter.addPageEpilog(buffer); + return buffer.toString(); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java index 989db1c0a59..41442d28b30 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java @@ -18,8 +18,11 @@ import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IPluginRegistry; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IAutoIndentStrategy; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.contentassist.ContentAssistant; @@ -27,6 +30,7 @@ import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.formatter.ContentFormatter; import org.eclipse.jface.text.formatter.IContentFormatter; import org.eclipse.jface.text.formatter.IFormattingStrategy; +import org.eclipse.jface.text.information.IInformationPresenter; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.reconciler.IReconciler; @@ -37,6 +41,8 @@ import org.eclipse.jface.text.rules.RuleBasedScanner; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.texteditor.ITextEditor; @@ -348,5 +354,28 @@ public class CSourceViewerConfiguration extends SourceViewerConfiguration { protected IPreferenceStore getPreferenceStore() { return CUIPlugin.getDefault().getPreferenceStore(); } + + /* + * @see SourceViewerConfiguration#getHoverControlCreator(ISourceViewer) + * @since 2.0 + */ + public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { + return getInformationControlCreator(sourceViewer, true); + } + + + public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer, final boolean cutDown) { + return new IInformationControlCreator() { + public IInformationControl createInformationControl(Shell parent) { + int style= cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL); + return new DefaultInformationControl(parent, style, new HTMLTextPresenter(cutDown)); + // return new HoverBrowserControl(parent); + } + }; + } + + public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) { + return super.getInformationPresenter(sourceViewer); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTML2TextReader.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTML2TextReader.java new file mode 100644 index 00000000000..1d8bfc62c6f --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTML2TextReader.java @@ -0,0 +1,255 @@ +package org.eclipse.cdt.internal.ui.text; + + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; + + +/** + * Reads the text contents from a reader of HTML contents and translates + * the tags or cut them out. + */ +public class HTML2TextReader extends SubstitutionTextReader { + + + private static final String LINE_DELIM= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + private static final Map fgEntityLookup; + private static final Set fgTags; + + static { + + fgTags= new HashSet(); + fgTags.add("b"); //$NON-NLS-1$ + fgTags.add("br"); //$NON-NLS-1$ + fgTags.add("h5"); //$NON-NLS-1$ + fgTags.add("p"); //$NON-NLS-1$ + fgTags.add("dl"); //$NON-NLS-1$ + fgTags.add("dt"); //$NON-NLS-1$ + fgTags.add("dd"); //$NON-NLS-1$ + fgTags.add("li"); //$NON-NLS-1$ + fgTags.add("ul"); //$NON-NLS-1$ + + fgEntityLookup= new HashMap(7); + fgEntityLookup.put("lt", "<"); //$NON-NLS-1$ //$NON-NLS-2$ + fgEntityLookup.put("gt", ">"); //$NON-NLS-1$ //$NON-NLS-2$ + fgEntityLookup.put("nbsp", " "); //$NON-NLS-1$ //$NON-NLS-2$ + fgEntityLookup.put("amp", "&"); //$NON-NLS-1$ //$NON-NLS-2$ + fgEntityLookup.put("circ", "^"); //$NON-NLS-1$ //$NON-NLS-2$ + fgEntityLookup.put("tilde", "~"); //$NON-NLS-2$ //$NON-NLS-1$ + fgEntityLookup.put("quot", "\""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private int fCounter= 0; + private TextPresentation fTextPresentation; + private int fBold= 0; + private int fStartOffset= -1; + private boolean fInParagraph= false; + + /** + * Transforms the html text from the reader to formatted text. + * @param presentation If not null, formattings will be applied to + * the presentation. + */ + public HTML2TextReader(Reader reader, TextPresentation presentation) { + super(new PushbackReader(reader)); + fTextPresentation= presentation; + } + + public int read() throws IOException { + int c= super.read(); + if (c != -1) + ++ fCounter; + return c; + } + + protected void startBold() { + if (fBold == 0) + fStartOffset= fCounter; + ++ fBold; + } + + protected void stopBold() { + -- fBold; + if (fBold == 0) { + if (fTextPresentation != null) { + fTextPresentation.addStyleRange(new StyleRange(fStartOffset, fCounter - fStartOffset, null, null, SWT.BOLD)); + } + fStartOffset= -1; + } + } + + /** + * @see SubstitutionTextReader#computeSubstitution(char) + */ + protected String computeSubstitution(int c) throws IOException { + if (c == '<') + return processHTMLTag(); + else if (c == '&') + return processEntity(); + + return null; + } + + private String html2Text(String html) { + + String tag= html; + if ('/' == tag.charAt(0)) + tag= tag.substring(1); + + if (!fgTags.contains(tag)) + return ""; //$NON-NLS-1$ + + if ("b".equals(html)) { //$NON-NLS-1$ + startBold(); + return ""; //$NON-NLS-1$ + } + + if ("h5".equals(html) || "dt".equals(html)) { //$NON-NLS-1$ //$NON-NLS-2$ + startBold(); + return ""; //$NON-NLS-1$ + } + + if ("dl".equals(html)) //$NON-NLS-1$ + return LINE_DELIM; + + if ("dd".equals(html)) //$NON-NLS-1$ + return "\t"; //$NON-NLS-1$ + + if ("li".equals(html)) //$NON-NLS-1$ + return LINE_DELIM + "\t" + "-"; + + if ("/b".equals(html)) { //$NON-NLS-1$ + stopBold(); + return ""; //$NON-NLS-1$ + } + + if ("p".equals(html)) { //$NON-NLS-1$ + fInParagraph= true; + return LINE_DELIM; + } + + if ("br".equals(html)) //$NON-NLS-1$ + return LINE_DELIM; + + if ("/p".equals(html)) { //$NON-NLS-1$ + boolean inParagraph= fInParagraph; + fInParagraph= false; + return inParagraph ? "" : LINE_DELIM; //$NON-NLS-1$ + } + + if ("/h5".equals(html) || "/dt".equals(html)) { //$NON-NLS-1$ //$NON-NLS-2$ + stopBold(); + return LINE_DELIM; + } + + if ("/dd".equals(html)) //$NON-NLS-1$ + return LINE_DELIM; + + return ""; //$NON-NLS-1$ + } + + /* + * A '<' has been read. Process a html tag + */ + private String processHTMLTag() throws IOException { + + StringBuffer buf= new StringBuffer(); + int ch; + do { + + ch= nextChar(); + + while (ch != -1 && ch != '>') { + buf.append(Character.toLowerCase((char) ch)); + ch= nextChar(); + if (ch == '"'){ + buf.append(Character.toLowerCase((char) ch)); + ch= nextChar(); + while (ch != -1 && ch != '"'){ + buf.append(Character.toLowerCase((char) ch)); + ch= nextChar(); + } + } + if (ch == '<'){ + unread(ch); + return '<' + buf.toString(); + } + } + + if (ch == -1) + return null; + + int tagLen= buf.length(); + // needs special treatment for comments + if ((tagLen >= 3 && "!--".equals(buf.substring(0, 3))) //$NON-NLS-1$ + && !(tagLen >= 5 && "--!".equals(buf.substring(tagLen - 3)))) { //$NON-NLS-1$ + // unfinished comment + buf.append(ch); + } else { + break; + } + } while (true); + + return html2Text(buf.toString()); + } + + private void unread(int ch) throws IOException { + ((PushbackReader) getReader()).unread(ch); + } + + protected String entity2Text(String symbol) { + if (symbol.length() > 1 && symbol.charAt(0) == '#') { + int ch; + try { + if (symbol.charAt(1) == 'x') { + ch= Integer.parseInt(symbol.substring(2), 16); + } else { + ch= Integer.parseInt(symbol.substring(1), 10); + } + return "" + (char)ch; //$NON-NLS-1$ + } catch (NumberFormatException e) { + } + } else { + String str= (String) fgEntityLookup.get(symbol); + if (str != null) { + return str; + } + } + return "&" + symbol; // not found //$NON-NLS-1$ + } + + /* + * A '&' has been read. Process a entity + */ + private String processEntity() throws IOException { + StringBuffer buf= new StringBuffer(); + int ch= nextChar(); + while (Character.isLetterOrDigit((char)ch) || ch == '#') { + buf.append((char) ch); + ch= nextChar(); + } + + if (ch == ';') + return entity2Text(buf.toString()); + + buf.insert(0, '&'); + if (ch != -1) + buf.append((char) ch); + return buf.toString(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLPrinter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLPrinter.java new file mode 100644 index 00000000000..70748b21002 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLPrinter.java @@ -0,0 +1,110 @@ +package org.eclipse.cdt.internal.ui.text; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + +import java.io.IOException; +import java.io.Reader; + + +/** + * Provides a set of convenience methods for creating HTML pages. + */ +public class HTMLPrinter { + + private HTMLPrinter() { + } + + private static String replace(String text, char c, String s) { + + int previous= 0; + int current= text.indexOf(c, previous); + + if (current == -1) + return text; + + StringBuffer buffer= new StringBuffer(); + while (current > -1) { + buffer.append(text.substring(previous, current)); + buffer.append(s); + previous= current + 1; + current= text.indexOf(c, previous); + } + buffer.append(text.substring(previous)); + + return buffer.toString(); + } + + public static String convertToHTMLContent(String content) { + content= replace(content, '<', "<"); //$NON-NLS-1$ + return replace(content, '>', ">"); //$NON-NLS-1$ + } + + public static String read(Reader rd) { + + StringBuffer buffer= new StringBuffer(); + char[] readBuffer= new char[2048]; + + try { + int n= rd.read(readBuffer); + while (n > 0) { + buffer.append(readBuffer, 0, n); + n= rd.read(readBuffer); + } + return buffer.toString(); + } catch (IOException x) { + } + + return null; + } + + public static void insertPageProlog(StringBuffer buffer, int position) { + buffer.insert(position, ""); //$NON-NLS-1$ + } + + public static void addPageProlog(StringBuffer buffer) { + insertPageProlog(buffer, buffer.length()); + } + + public static void addPageEpilog(StringBuffer buffer) { + buffer.append(""); //$NON-NLS-1$ + } + + public static void startBulletList(StringBuffer buffer) { + buffer.append(""); //$NON-NLS-1$ + } + + public static void addBullet(StringBuffer buffer, String bullet) { + if (bullet != null) { + buffer.append("
  • "); //$NON-NLS-1$ + buffer.append(bullet); + buffer.append("
  • "); //$NON-NLS-1$ + } + } + + public static void addSmallHeader(StringBuffer buffer, String header) { + if (header != null) { + buffer.append("
    "); //$NON-NLS-1$ + buffer.append(header); + buffer.append("
    "); //$NON-NLS-1$ + } + } + + public static void addParagraph(StringBuffer buffer, String paragraph) { + if (paragraph != null) { + buffer.append("

    "); //$NON-NLS-1$ + buffer.append(paragraph); + } + } + + public static void addParagraph(StringBuffer buffer, Reader paragraphReader) { + if (paragraphReader != null) + addParagraph(buffer, read(paragraphReader)); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLTextPresenter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLTextPresenter.java new file mode 100644 index 00000000000..d809348e1fb --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/HTMLTextPresenter.java @@ -0,0 +1,185 @@ +package org.eclipse.cdt.internal.ui.text; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import java.util.Iterator; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; + + +public class HTMLTextPresenter implements DefaultInformationControl.IInformationPresenter { + + private static final String LINE_DELIM= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + + private int fCounter; + private boolean fEnforceUpperLineLimit; + + public HTMLTextPresenter(boolean enforceUpperLineLimit) { + super(); + fEnforceUpperLineLimit= enforceUpperLineLimit; + } + + public HTMLTextPresenter() { + this(true); + } + + protected Reader createReader(String hoverInfo, TextPresentation presentation) { + return new HTML2TextReader(new StringReader(hoverInfo), presentation); + } + + protected void adaptTextPresentation(TextPresentation presentation, int offset, int insertLength) { + + int yoursStart= offset; + int yoursEnd= offset + insertLength -1; + yoursEnd= Math.max(yoursStart, yoursEnd); + + Iterator e= presentation.getAllStyleRangeIterator(); + while (e.hasNext()) { + + StyleRange range= (StyleRange) e.next(); + + int myStart= range.start; + int myEnd= range.start + range.length -1; + myEnd= Math.max(myStart, myEnd); + + if (myEnd < yoursStart) + continue; + + if (myStart < yoursStart) + range.length += insertLength; + else + range.start += insertLength; + } + } + + private void append(StringBuffer buffer, String string, TextPresentation presentation) { + + int length= string.length(); + buffer.append(string); + + if (presentation != null) + adaptTextPresentation(presentation, fCounter, length); + + fCounter += length; + } + + private String getIndent(String line) { + int length= line.length(); + + int i= 0; + while (i < length && Character.isWhitespace(line.charAt(i))) ++i; + + return (i == length ? line : line.substring(0, i)) + " "; //$NON-NLS-1$ + } + + /* + * @see IHoverInformationPresenter#updatePresentation(Display display, String, TextPresentation, int, int) + */ + public String updatePresentation(Display display, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) { + + if (hoverInfo == null) + return null; + + GC gc= new GC(display); + try { + + StringBuffer buffer= new StringBuffer(); + int maxNumberOfLines= Math.round(maxHeight / gc.getFontMetrics().getHeight()); + + fCounter= 0; + LineBreakingReader reader= new LineBreakingReader(createReader(hoverInfo, presentation), gc, maxWidth); + + boolean lastLineFormatted= false; + String lastLineIndent= null; + + String line=reader.readLine(); + boolean lineFormatted= reader.isFormattedLine(); + boolean firstLineProcessed= false; + + while (line != null) { + + if (fEnforceUpperLineLimit && maxNumberOfLines <= 0) + break; + + if (firstLineProcessed) { + if (!lastLineFormatted) + append(buffer, LINE_DELIM, null); + else { + append(buffer, LINE_DELIM, presentation); + if (lastLineIndent != null) + append(buffer, lastLineIndent, presentation); + } + } + + append(buffer, line, null); + firstLineProcessed= true; + + lastLineFormatted= lineFormatted; + if (!lineFormatted) + lastLineIndent= null; + else if (lastLineIndent == null) + lastLineIndent= getIndent(line); + + line= reader.readLine(); + lineFormatted= reader.isFormattedLine(); + + maxNumberOfLines--; + } + + if (line != null) { + append(buffer, LINE_DELIM, lineFormatted ? presentation : null); + append(buffer, (""), presentation); + } + + return trim(buffer, presentation); + + } catch (IOException e) { + + CUIPlugin.log(e); + return null; + + } finally { + gc.dispose(); + } + } + + private String trim(StringBuffer buffer, TextPresentation presentation) { + + int length= buffer.length(); + + int end= length -1; + while (end >= 0 && Character.isWhitespace(buffer.charAt(end))) + -- end; + + if (end == -1) + return ""; //$NON-NLS-1$ + + if (end < length -1) + buffer.delete(end + 1, length); + else + end= length; + + int start= 0; + while (start < end && Character.isWhitespace(buffer.charAt(start))) + ++ start; + + buffer.delete(0, start); + presentation.setResultWindow(new Region(start, buffer.length())); + return buffer.toString(); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/LineBreakingReader.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/LineBreakingReader.java index 2844da78b62..ab2683af5cf 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/LineBreakingReader.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/LineBreakingReader.java @@ -65,6 +65,11 @@ public class LineBreakingReader { fLine= null; fLineBreakIterator= BreakIterator.getLineInstance(); } + + public boolean isFormattedLine() { + return fLine != null; + } + /** * Reads the next line. The lengths of the line will not exceed the gived maximum * width. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SubstitutionTextReader.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SubstitutionTextReader.java new file mode 100644 index 00000000000..178482a458f --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SubstitutionTextReader.java @@ -0,0 +1,134 @@ +package org.eclipse.cdt.internal.ui.text; + + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.io.IOException; +import java.io.Reader; + + +/** + * Reads the text contents from a reader and computes for each character + * a potential substitution. The substitution may eat more characters than + * only the one passed into the computation routine. + */ +public abstract class SubstitutionTextReader extends SingleCharReader { + + protected static final String LINE_DELIM= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + + private Reader fReader; + private boolean fWasWhiteSpace; + private int fCharAfterWhiteSpace; + + private boolean fReadFromBuffer; + private StringBuffer fBuffer; + private int fIndex; + + + protected SubstitutionTextReader(Reader reader) { + fReader= reader; + fBuffer= new StringBuffer(); + fIndex= 0; + fReadFromBuffer= false; + fCharAfterWhiteSpace= -1; + fWasWhiteSpace= true; + } + + /** + * Implement to compute the substitution for the given character and + * if necessary subsequent characters. Use nextChar + * to read subsequent characters. + */ + protected abstract String computeSubstitution(int c) throws IOException; + + /** + * Returns the internal reader. + */ + protected Reader getReader() { + return fReader; + } + + /** + * Returns the next character. + */ + protected int nextChar() throws IOException { + fReadFromBuffer= (fBuffer.length() > 0); + if (fReadFromBuffer) { + char ch= fBuffer.charAt(fIndex++); + if (fIndex >= fBuffer.length()) { + fBuffer.setLength(0); + fIndex= 0; + } + return ch; + } else { + int ch= fCharAfterWhiteSpace; + if (ch == -1) { + ch= fReader.read(); + } + if (Character.isWhitespace((char)ch)) { + do { + ch= fReader.read(); + } while (Character.isWhitespace((char)ch)); + if (ch != -1) { + fCharAfterWhiteSpace= ch; + return ' '; + } + } else { + fCharAfterWhiteSpace= -1; + } + return ch; + } + } + + /** + * @see Reader#read() + */ + public int read() throws IOException { + int c; + do { + + c= nextChar(); + while (!fReadFromBuffer) { + String s= computeSubstitution(c); + if (s == null) + break; + if (s.length() > 0) + fBuffer.insert(0, s); + c= nextChar(); + } + + } while (fWasWhiteSpace && (c == ' ')); + + fWasWhiteSpace= (c == ' ' || c == '\r' || c == '\n'); + return c; + } + + /** + * @see Reader#ready() + */ + public boolean ready() throws IOException { + return fReader.ready(); + } + + /** + * @see Reader#close() + */ + public void close() throws IOException { + fReader.close(); + } + + /** + * @see Reader#reset() + */ + public void reset() throws IOException { + fReader.reset(); + fWasWhiteSpace= true; + fCharAfterWhiteSpace= -1; + fBuffer.setLength(0); + fIndex= 0; + } +} \ No newline at end of file