1 /*******************************************************************************
2 * Copyright (c) 2005, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.ui.text.java;
13 import java.io.BufferedReader;
14 import java.io.IOException;
15 import java.io.InputStreamReader;
18 import org.osgi.framework.Bundle;
20 import org.eclipse.osgi.util.TextProcessor;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.custom.StyleRange;
24 import org.eclipse.swt.custom.StyledText;
25 import org.eclipse.swt.events.VerifyEvent;
26 import org.eclipse.swt.graphics.Color;
27 import org.eclipse.swt.graphics.FontData;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.RGB;
31 import org.eclipse.swt.widgets.Shell;
33 import org.eclipse.core.runtime.Assert;
34 import org.eclipse.core.runtime.FileLocator;
35 import org.eclipse.core.runtime.IProgressMonitor;
36 import org.eclipse.core.runtime.NullProgressMonitor;
37 import org.eclipse.core.runtime.Platform;
39 import org.eclipse.jface.internal.text.html.BrowserInformationControl;
40 import org.eclipse.jface.internal.text.html.HTMLPrinter;
41 import org.eclipse.jface.preference.IPreferenceStore;
42 import org.eclipse.jface.preference.PreferenceConverter;
43 import org.eclipse.jface.resource.JFaceResources;
44 import org.eclipse.jface.viewers.StyledString;
46 import org.eclipse.jface.text.BadLocationException;
47 import org.eclipse.jface.text.BadPositionCategoryException;
48 import org.eclipse.jface.text.DefaultPositionUpdater;
49 import org.eclipse.jface.text.DocumentCommand;
50 import org.eclipse.jface.text.DocumentEvent;
51 import org.eclipse.jface.text.IDocument;
52 import org.eclipse.jface.text.IInformationControlCreator;
53 import org.eclipse.jface.text.IPositionUpdater;
54 import org.eclipse.jface.text.IRegion;
55 import org.eclipse.jface.text.ITextPresentationListener;
56 import org.eclipse.jface.text.ITextViewer;
57 import org.eclipse.jface.text.ITextViewerExtension2;
58 import org.eclipse.jface.text.ITextViewerExtension4;
59 import org.eclipse.jface.text.ITextViewerExtension5;
60 import org.eclipse.jface.text.Position;
61 import org.eclipse.jface.text.TextPresentation;
62 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
63 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
64 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
65 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
66 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
67 import org.eclipse.jface.text.contentassist.IContextInformation;
68 import org.eclipse.jface.text.link.ILinkedModeListener;
69 import org.eclipse.jface.text.link.LinkedModeModel;
70 import org.eclipse.jface.text.link.LinkedModeUI;
71 import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
72 import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
73 import org.eclipse.jface.text.link.LinkedPosition;
74 import org.eclipse.jface.text.link.LinkedPositionGroup;
76 import org.eclipse.ui.IWorkbenchPage;
77 import org.eclipse.ui.IWorkbenchPart;
78 import org.eclipse.ui.IWorkbenchSite;
80 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
82 import org.eclipse.jdt.core.CompletionProposal;
83 import org.eclipse.jdt.core.IJavaElement;
84 import org.eclipse.jdt.core.IMember;
85 import org.eclipse.jdt.core.JavaCore;
86 import org.eclipse.jdt.core.JavaModelException;
87 import org.eclipse.jdt.core.compiler.CharOperation;
89 import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations;
91 import org.eclipse.jdt.ui.PreferenceConstants;
92 import org.eclipse.jdt.ui.text.IJavaPartitions;
93 import org.eclipse.jdt.ui.text.JavaTextTools;
94 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
95 import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
97 import org.eclipse.jdt.internal.ui.JavaPlugin;
98 import org.eclipse.jdt.internal.ui.text.java.hover.JavadocBrowserInformationControlInput;
99 import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover;
106 public abstract class AbstractJavaCompletionProposal implements IJavaCompletionProposal, ICompletionProposalExtension, ICompletionProposalExtension2, ICompletionProposalExtension3, ICompletionProposalExtension5, ICompletionProposalExtension6 {
110 * A class to simplify tracking a reference position in a document.
112 static final class ReferenceTracker {
114 /** The reference position category name. */
115 private static final String CATEGORY= "reference_position"; //$NON-NLS-1$
116 /** The position updater of the reference position. */
117 private final IPositionUpdater fPositionUpdater= new DefaultPositionUpdater(CATEGORY);
118 /** The reference position. */
119 private final Position fPosition= new Position(0);
122 * Called before document changes occur. It must be followed by a call to postReplace().
124 * @param document the document on which to track the reference position.
125 * @param offset the offset
126 * @throws BadLocationException if the offset describes an invalid range in this document
129 public void preReplace(IDocument document, int offset) throws BadLocationException {
130 fPosition.setOffset(offset);
132 document.addPositionCategory(CATEGORY);
133 document.addPositionUpdater(fPositionUpdater);
134 document.addPosition(CATEGORY, fPosition);
136 } catch (BadPositionCategoryException e) {
143 * Called after the document changed occurred. It must be preceded by a call to preReplace().
145 * @param document the document on which to track the reference position.
146 * @return offset after the replace
148 public int postReplace(IDocument document) {
150 document.removePosition(CATEGORY, fPosition);
151 document.removePositionUpdater(fPositionUpdater);
152 document.removePositionCategory(CATEGORY);
154 } catch (BadPositionCategoryException e) {
158 return fPosition.getOffset();
162 protected static class ExitPolicy implements IExitPolicy {
164 final char fExitCharacter;
165 private final IDocument fDocument;
167 public ExitPolicy(char exitCharacter, IDocument document) {
168 fExitCharacter= exitCharacter;
173 * @see org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, org.eclipse.swt.events.VerifyEvent, int, int)
175 public ExitFlags doExit(LinkedModeModel environment, VerifyEvent event, int offset, int length) {
177 if (event.character == fExitCharacter) {
178 if (environment.anyPositionContains(offset))
179 return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
181 return new ExitFlags(ILinkedModeListener.UPDATE_CARET, true);
184 switch (event.character) {
186 return new ExitFlags(ILinkedModeListener.NONE, true);
188 // when entering an anonymous class as a parameter, we don't want
189 // to jump after the parenthesis when return is pressed
192 if (fDocument.getChar(offset - 1) == '{')
193 return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
194 } catch (BadLocationException e) {
205 private StyledString fDisplayString;
206 private String fReplacementString;
207 private int fReplacementOffset;
208 private int fReplacementLength;
209 private int fCursorPosition;
210 private Image fImage;
211 private IContextInformation fContextInformation;
212 private ProposalInfo fProposalInfo;
213 private char[] fTriggerCharacters;
214 private String fSortString;
215 private int fRelevance;
216 private boolean fIsInJavadoc;
218 private StyleRange fRememberedStyleRange;
220 private boolean fToggleEating;
221 private ITextViewer fTextViewer;
225 * The control creator.
229 private IInformationControlCreator fCreator;
231 * The CSS used to format javadoc information.
234 private static String fgCSSStyles;
237 * The invocation context of this completion proposal. Can be <code>null</code>.
239 public final JavaContentAssistInvocationContext fInvocationContext;
242 * Cache to store last validation state.
245 private boolean fIsValidated= true;
248 * The text presentation listener.
251 private ITextPresentationListener fTextPresentationListener;
253 protected AbstractJavaCompletionProposal() {
254 fInvocationContext= null;
257 protected AbstractJavaCompletionProposal(JavaContentAssistInvocationContext context) {
258 fInvocationContext= context;
262 * @see ICompletionProposalExtension#getTriggerCharacters()
264 public char[] getTriggerCharacters() {
265 return fTriggerCharacters;
269 * Sets the trigger characters.
271 * @param triggerCharacters The set of characters which can trigger the application of this
272 * completion proposal
274 public void setTriggerCharacters(char[] triggerCharacters) {
275 fTriggerCharacters= triggerCharacters;
279 * Sets the proposal info.
281 * @param proposalInfo The additional information associated with this proposal or
284 public void setProposalInfo(ProposalInfo proposalInfo) {
285 fProposalInfo= proposalInfo;
289 * Returns the additional proposal info, or <code>null</code> if none exists.
291 * @return the additional proposal info, or <code>null</code> if none exists
293 protected ProposalInfo getProposalInfo() {
294 return fProposalInfo;
298 * Sets the cursor position relative to the insertion offset. By default this is the length of
299 * the completion string (Cursor positioned after the completion)
301 * @param cursorPosition The cursorPosition to set
303 public void setCursorPosition(int cursorPosition) {
304 Assert.isTrue(cursorPosition >= 0);
305 fCursorPosition= cursorPosition;
308 protected int getCursorPosition() {
309 return fCursorPosition;
313 * @see ICompletionProposal#apply
315 public final void apply(IDocument document) {
316 // not used any longer
317 apply(document, (char) 0, getReplacementOffset() + getReplacementLength());
321 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#apply(org.eclipse.jface.text.IDocument, char, int)
323 public void apply(IDocument document, char trigger, int offset) {
325 if (isSupportingRequiredProposals()) {
326 CompletionProposal coreProposal= ((MemberProposalInfo)getProposalInfo()).fProposal;
327 CompletionProposal[] requiredProposals= coreProposal.getRequiredProposals();
328 for (int i= 0; requiredProposals != null && i < requiredProposals.length; i++) {
329 int oldLen= document.getLength();
330 if (requiredProposals[i].getKind() == CompletionProposal.TYPE_REF) {
331 LazyJavaCompletionProposal proposal= createRequiredTypeCompletionProposal(requiredProposals[i], fInvocationContext);
332 proposal.apply(document);
333 setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
335 fInvocationContext.generated_3345393073719118376(this, document, coreProposal, requiredProposals, i, oldLen);
340 boolean isSmartTrigger= isSmartTrigger(trigger);
343 if (isSmartTrigger || trigger == (char) 0) {
344 replacement= getReplacementString();
346 StringBuffer buffer= new StringBuffer(getReplacementString());
348 // fix for PR #5533. Assumes that no eating takes place.
349 if ((getCursorPosition() > 0 && getCursorPosition() <= buffer.length() && buffer.charAt(getCursorPosition() - 1) != trigger)) {
350 buffer.insert(getCursorPosition(), trigger);
351 setCursorPosition(getCursorPosition() + 1);
354 replacement= buffer.toString();
355 setReplacementString(replacement);
358 // reference position just at the end of the document change.
359 int referenceOffset= getReplacementOffset() + getReplacementLength();
360 final ReferenceTracker referenceTracker= new ReferenceTracker();
361 referenceTracker.preReplace(document, referenceOffset);
363 replace(document, getReplacementOffset(), getReplacementLength(), replacement);
365 referenceOffset= referenceTracker.postReplace(document);
366 setReplacementOffset(referenceOffset - (replacement == null ? 0 : replacement.length()));
370 handleSmartTrigger(document, trigger, referenceOffset);
372 } catch (BadLocationException x) {
378 * Creates the required type proposal.
380 * @param completionProposal the core completion proposal
381 * @param invocationContext invocation context
382 * @return the required type completion proposal
385 protected LazyJavaCompletionProposal createRequiredTypeCompletionProposal(CompletionProposal completionProposal, JavaContentAssistInvocationContext invocationContext) {
386 if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES))
387 return (LazyJavaCompletionProposal)new FillArgumentNamesCompletionProposalCollector(invocationContext).createJavaCompletionProposal(completionProposal);
389 return new LazyJavaTypeCompletionProposal(completionProposal, invocationContext);
392 private boolean isSmartTrigger(char trigger) {
393 return trigger == ';' && JavaPlugin.getDefault().getCombinedPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_SEMICOLON)
394 || trigger == '{' && JavaPlugin.getDefault().getCombinedPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_OPENING_BRACE);
397 private void handleSmartTrigger(IDocument document, char trigger, int referenceOffset) throws BadLocationException {
398 DocumentCommand cmd= new DocumentCommand() {
401 cmd.offset= referenceOffset;
403 cmd.text= Character.toString(trigger);
405 cmd.shiftsCaret= true;
406 cmd.caretOffset= getReplacementOffset() + getCursorPosition();
408 SmartSemicolonAutoEditStrategy strategy= new SmartSemicolonAutoEditStrategy(IJavaPartitions.JAVA_PARTITIONING);
409 strategy.customizeDocumentCommand(document, cmd);
411 replace(document, cmd.offset, cmd.length, cmd.text);
412 setCursorPosition(cmd.caretOffset - getReplacementOffset() + cmd.text.length());
415 protected final void replace(IDocument document, int offset, int length, String string) throws BadLocationException {
416 if (!document.get(offset, length).equals(string))
417 document.replace(offset, length, string);
421 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension1#apply(org.eclipse.jface.text.ITextViewer, char, int, int)
423 public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
425 IDocument document= viewer.getDocument();
426 if (fTextViewer == null)
429 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96059
430 // don't apply the proposal if for some reason we're not valid any longer
431 if (!isInJavadoc() && !validate(document, offset, null)) {
432 setCursorPosition(offset);
433 if (trigger != '\0') {
435 document.replace(offset, 0, String.valueOf(trigger));
436 setCursorPosition(getCursorPosition() + 1);
437 if (trigger == '(' && autocloseBrackets()) {
438 document.replace(getReplacementOffset() + getCursorPosition(), 0, ")"); //$NON-NLS-1$
439 setUpLinkedMode(document, ')');
441 } catch (BadLocationException x) {
448 // don't eat if not in preferences, XOR with Ctrl
449 // but: if there is a selection, replace it!
450 Point selection= viewer.getSelectedRange();
451 fToggleEating= (stateMask & SWT.CTRL) != 0;
452 int newLength= selection.x + selection.y - getReplacementOffset();
453 if ((insertCompletion() ^ fToggleEating) && newLength >= 0)
454 setReplacementLength(newLength);
456 apply(document, trigger, offset);
457 fToggleEating= false;
461 * Tells whether the user toggled the insert mode by pressing the 'Ctrl' key.
463 * @return <code>true</code> if the insert mode is toggled, <code>false</code> otherwise
466 protected boolean isInsertModeToggled() {
467 return fToggleEating;
471 * Returns <code>true</code> if the proposal is within javadoc, <code>false</code> otherwise.
473 * @return <code>true</code> if the proposal is within javadoc, <code>false</code> otherwise
475 protected boolean isInJavadoc() {
480 * Sets the javadoc attribute.
482 * @param isInJavadoc <code>true</code> if the proposal is within javadoc
484 protected void setInJavadoc(boolean isInJavadoc) {
485 fIsInJavadoc= isInJavadoc;
489 * @see ICompletionProposal#getSelection
491 public Point getSelection(IDocument document) {
494 return new Point(getReplacementOffset() + getCursorPosition(), 0);
498 * @see ICompletionProposal#getContextInformation()
500 public IContextInformation getContextInformation() {
501 return fContextInformation;
505 * Sets the context information.
506 * @param contextInformation The context information associated with this proposal
508 public void setContextInformation(IContextInformation contextInformation) {
509 fContextInformation= contextInformation;
513 * @see ICompletionProposal#getDisplayString()
515 public String getDisplayString() {
516 if (fDisplayString != null)
517 return fDisplayString.getString();
518 return ""; //$NON-NLS-1$
522 * @see ICompletionProposal#getAdditionalProposalInfo()
524 public String getAdditionalProposalInfo() {
525 Object info= getAdditionalProposalInfo(new NullProgressMonitor());
526 return info == null ? null : info.toString();
530 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension5#getAdditionalProposalInfo(org.eclipse.core.runtime.IProgressMonitor)
532 public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
533 if (getProposalInfo() != null) {
534 String info= getProposalInfo().getInfo(monitor);
535 if (info != null && info.length() > 0) {
536 StringBuffer buffer= new StringBuffer();
537 HTMLPrinter.insertPageProlog(buffer, 0, getCSSStyles());
541 IJavaElement element= null;
543 element= getProposalInfo().getJavaElement();
544 if (element instanceof IMember) {
545 String base= JavaDocLocations.getBaseURL((IMember) element);
547 int endHeadIdx= buffer.indexOf("</head>"); //$NON-NLS-1$
548 buffer.insert(endHeadIdx, "\n<base href='" + base + "'>\n"); //$NON-NLS-1$ //$NON-NLS-2$
551 } catch (JavaModelException e) {
555 HTMLPrinter.addPageEpilog(buffer);
556 info= buffer.toString();
558 return new JavadocBrowserInformationControlInput(null, element, info, 0);
565 * Returns the style information for displaying HTML (Javadoc) content.
567 * @return the CSS styles
570 protected String getCSSStyles() {
571 if (fgCSSStyles == null) {
572 Bundle bundle= Platform.getBundle(JavaPlugin.getPluginId());
573 URL url= bundle.getEntry("/JavadocHoverStyleSheet.css"); //$NON-NLS-1$
575 BufferedReader reader= null;
577 url= FileLocator.toFileURL(url);
578 reader= new BufferedReader(new InputStreamReader(url.openStream()));
579 StringBuffer buffer= new StringBuffer(200);
580 String line= reader.readLine();
581 while (line != null) {
584 line= reader.readLine();
586 fgCSSStyles= buffer.toString();
587 } catch (IOException ex) {
593 } catch (IOException e) {
599 String css= fgCSSStyles;
601 FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0];
602 css= HTMLPrinter.convertTopLevelFont(css, fontData);
608 * @see ICompletionProposalExtension#getContextInformationPosition()
610 public int getContextInformationPosition() {
611 if (getContextInformation() == null)
612 return getReplacementOffset() - 1;
613 return getReplacementOffset() + getCursorPosition();
617 * Gets the replacement offset.
618 * @return Returns a int
620 public int getReplacementOffset() {
621 return fReplacementOffset;
625 * Sets the replacement offset.
626 * @param replacementOffset The replacement offset to set
628 public void setReplacementOffset(int replacementOffset) {
629 Assert.isTrue(replacementOffset >= 0);
630 fReplacementOffset= replacementOffset;
634 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getCompletionOffset()
636 public int getPrefixCompletionStart(IDocument document, int completionOffset) {
637 return getReplacementOffset();
641 * Gets the replacement length.
642 * @return Returns a int
644 public int getReplacementLength() {
645 return fReplacementLength;
649 * Sets the replacement length.
650 * @param replacementLength The replacementLength to set
652 public void setReplacementLength(int replacementLength) {
653 Assert.isTrue(replacementLength >= 0);
654 fReplacementLength= replacementLength;
658 * Gets the replacement string.
659 * @return Returns a String
661 public String getReplacementString() {
662 return fReplacementString;
666 * Sets the replacement string.
667 * @param replacementString The replacement string to set
669 public void setReplacementString(String replacementString) {
670 Assert.isNotNull(replacementString);
671 fReplacementString= replacementString;
675 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementText()
677 public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
678 if (!isCamelCaseMatching())
679 return getReplacementString();
681 String prefix= getPrefix(document, completionOffset);
682 return getCamelCaseCompound(prefix, getReplacementString());
686 * @see ICompletionProposal#getImage()
688 public Image getImage() {
694 * @param image The image to set
696 public void setImage(Image image) {
701 * @see ICompletionProposalExtension#isValidFor(IDocument, int)
703 public boolean isValidFor(IDocument document, int offset) {
704 return validate(document, offset, null);
708 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument, int, org.eclipse.jface.text.DocumentEvent)
710 public boolean validate(IDocument document, int offset, DocumentEvent event) {
712 if (!isOffsetValid(offset))
713 return fIsValidated= false;
715 fIsValidated= isValidPrefix(getPrefix(document, offset));
717 if (fIsValidated && event != null) {
718 // adapt replacement range to document change
719 int delta= (event.fText == null ? 0 : event.fText.length()) - event.fLength;
720 final int newLength= Math.max(getReplacementLength() + delta, 0);
721 setReplacementLength(newLength);
728 * Checks whether the given offset is valid for this proposal.
730 * @param offset the caret offset
731 * @return <code>true</code> if the offset is valid for this proposal
734 protected boolean isOffsetValid(int offset) {
735 return getReplacementOffset() <= offset;
739 * Checks whether <code>prefix</code> is a valid prefix for this proposal. Usually, while code
740 * completion is in progress, the user types and edits the prefix in the document in order to
741 * filter the proposal list. From {@link #validate(IDocument, int, DocumentEvent) }, the
742 * current prefix in the document is extracted and this method is called to find out whether the
743 * proposal is still valid.
745 * The default implementation checks if <code>prefix</code> is a prefix of the proposal's
746 * {@link #getDisplayString() display string} using the {@link #isPrefix(String, String) }
750 * @param prefix the current prefix in the document
751 * @return <code>true</code> if <code>prefix</code> is a valid prefix of this proposal
753 protected boolean isValidPrefix(String prefix) {
755 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=17667
756 * why we do not use the replacement string.
757 * String word= fReplacementString;
759 * Besides that bug we also use the display string
760 * for performance reasons, as computing the
761 * replacement string can be expensive.
763 return isPrefix(prefix, TextProcessor.deprocess(getDisplayString()));
767 * Gets the proposal's relevance.
768 * @return Returns a int
770 public int getRelevance() {
775 * Sets the proposal's relevance.
776 * @param relevance The relevance to set
778 public void setRelevance(int relevance) {
779 fRelevance= relevance;
783 * Returns the text in <code>document</code> from {@link #getReplacementOffset()} to
784 * <code>offset</code>. Returns the empty string if <code>offset</code> is before the
785 * replacement offset or if an exception occurs when accessing the document.
787 * @param document the document
788 * @param offset the offset
792 protected String getPrefix(IDocument document, int offset) {
794 int length= offset - getReplacementOffset();
796 return document.get(getReplacementOffset(), length);
797 } catch (BadLocationException x) {
799 return ""; //$NON-NLS-1$
803 * Case insensitive comparison of <code>prefix</code> with the start of <code>string</code>.
805 * @param prefix the prefix
806 * @param string the string to look for the prefix
807 * @return <code>true</code> if the string begins with the given prefix and
808 * <code>false</code> if <code>prefix</code> is longer than <code>string</code>
809 * or the string doesn't start with the given prefix
812 protected boolean isPrefix(String prefix, String string) {
813 if (prefix == null || string ==null || prefix.length() > string.length())
815 String start= string.substring(0, prefix.length());
816 return start.equalsIgnoreCase(prefix) || isCamelCaseMatching() && CharOperation.camelCaseMatch(prefix.toCharArray(), string.toCharArray());
820 * Matches <code>prefix</code> against <code>string</code> and replaces the matched region
821 * by prefix. Case is preserved as much as possible. This method returns <code>string</code> if camel case completion
822 * is disabled. Examples when camel case completion is enabled:
824 * <li>getCamelCompound("NuPo", "NullPointerException") -> "NuPointerException"</li>
825 * <li>getCamelCompound("NuPoE", "NullPointerException") -> "NuPoException"</li>
826 * <li>getCamelCompound("hasCod", "hashCode") -> "hasCode"</li>
829 * @param prefix the prefix to match against
830 * @param string the string to match
831 * @return a compound of prefix and any postfix taken from <code>string</code>
834 protected final String getCamelCaseCompound(String prefix, String string) {
835 if (prefix.length() > string.length())
838 // a normal prefix - no camel case logic at all
839 String start= string.substring(0, prefix.length());
840 if (start.equalsIgnoreCase(prefix))
843 final char[] patternChars= prefix.toCharArray();
844 final char[] stringChars= string.toCharArray();
846 for (int i= 1; i <= stringChars.length; i++)
847 if (CharOperation.camelCaseMatch(patternChars, 0, patternChars.length, stringChars, 0, i))
848 return prefix + string.substring(i);
850 // Not a camel case match at all.
851 // This should not happen -> stay with the default behavior
856 * Returns true if camel case matching is enabled.
858 * @return <code>true</code> if camel case matching is enabled
861 protected boolean isCamelCaseMatching() {
862 String value= JavaCore.getOption(JavaCore.CODEASSIST_CAMEL_CASE_MATCH);
863 return JavaCore.ENABLED.equals(value);
866 protected static boolean insertCompletion() {
867 IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
868 return preference.getBoolean(PreferenceConstants.CODEASSIST_INSERT_COMPLETION);
871 private static Color getForegroundColor() {
872 IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
873 RGB rgb= PreferenceConverter.getColor(preference, PreferenceConstants.CODEASSIST_REPLACEMENT_FOREGROUND);
874 JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
875 return textTools.getColorManager().getColor(rgb);
878 private static Color getBackgroundColor() {
879 IPreferenceStore preference= JavaPlugin.getDefault().getPreferenceStore();
880 RGB rgb= PreferenceConverter.getColor(preference, PreferenceConstants.CODEASSIST_REPLACEMENT_BACKGROUND);
881 JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
882 return textTools.getColorManager().getColor(rgb);
885 private void repairPresentation(ITextViewer viewer) {
886 if (fRememberedStyleRange != null) {
887 if (viewer instanceof ITextViewerExtension2) {
888 // attempts to reduce the redraw area
889 ITextViewerExtension2 viewer2= (ITextViewerExtension2)viewer;
890 viewer2.invalidateTextPresentation(fRememberedStyleRange.start, fRememberedStyleRange.length);
892 viewer.invalidateTextPresentation();
896 private void updateStyle(ITextViewer viewer) {
897 StyledText text= viewer.getTextWidget();
898 int widgetOffset= getWidgetOffset(viewer, fRememberedStyleRange.start);
899 StyleRange range= new StyleRange(fRememberedStyleRange);
900 range.start= widgetOffset;
901 range.length= fRememberedStyleRange.length;
902 StyleRange currentRange= text.getStyleRangeAtOffset(widgetOffset);
903 if (currentRange != null) {
904 range.strikeout= currentRange.strikeout;
905 range.underline= currentRange.underline;
906 range.fontStyle= currentRange.fontStyle;
909 // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
911 text.setStyleRange(range);
912 } catch (IllegalArgumentException x) {
913 // catching exception as offset + length might be outside of the text widget
914 fRememberedStyleRange= null;
919 * Convert a document offset to the corresponding widget offset.
921 * @param viewer the text viewer
922 * @param documentOffset the document offset
923 * @return widget offset
926 private int getWidgetOffset(ITextViewer viewer, int documentOffset) {
927 if (viewer instanceof ITextViewerExtension5) {
928 ITextViewerExtension5 extension= (ITextViewerExtension5)viewer;
929 return extension.modelOffset2WidgetOffset(documentOffset);
931 IRegion visible= viewer.getVisibleRegion();
932 int widgetOffset= documentOffset - visible.getOffset();
933 if (widgetOffset > visible.getLength()) {
941 * Creates a style range for the text viewer.
943 * @param viewer the text viewer
944 * @return the new style range for the text viewer or <code>null</code>
947 private StyleRange createStyleRange(ITextViewer viewer) {
948 StyledText text= viewer.getTextWidget();
949 if (text == null || text.isDisposed())
952 int widgetCaret= text.getCaretOffset();
955 if (viewer instanceof ITextViewerExtension5) {
956 ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
957 modelCaret= extension.widgetOffset2ModelOffset(widgetCaret);
959 IRegion visibleRegion= viewer.getVisibleRegion();
960 modelCaret= widgetCaret + visibleRegion.getOffset();
963 if (modelCaret >= getReplacementOffset() + getReplacementLength())
966 int length= getReplacementOffset() + getReplacementLength() - modelCaret;
968 Color foreground= getForegroundColor();
969 Color background= getBackgroundColor();
971 return new StyleRange(modelCaret, length, foreground, background);
975 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(ITextViewer, boolean)
977 public void selected(final ITextViewer viewer, boolean smartToggle) {
978 repairPresentation(viewer);
979 fRememberedStyleRange= null;
981 if (!insertCompletion() ^ smartToggle) {
982 StyleRange range= createStyleRange(viewer);
986 fRememberedStyleRange= range;
988 if (viewer instanceof ITextViewerExtension4) {
989 if (fTextPresentationListener == null) {
990 fTextPresentationListener= new ITextPresentationListener() {
992 * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
994 public void applyTextPresentation(TextPresentation textPresentation) {
995 fRememberedStyleRange= createStyleRange(viewer);
996 if (fRememberedStyleRange != null)
997 textPresentation.mergeStyleRange(fRememberedStyleRange);
1000 ((ITextViewerExtension4)viewer).addTextPresentationListener(fTextPresentationListener);
1002 repairPresentation(viewer);
1004 updateStyle(viewer);
1009 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(ITextViewer)
1011 public void unselected(ITextViewer viewer) {
1012 if (fTextPresentationListener != null) {
1013 ((ITextViewerExtension4)viewer).removeTextPresentationListener(fTextPresentationListener);
1014 fTextPresentationListener= null;
1016 repairPresentation(viewer);
1017 fRememberedStyleRange= null;
1021 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
1023 public IInformationControlCreator getInformationControlCreator() {
1024 Shell shell= JavaPlugin.getActiveWorkbenchShell();
1025 if (shell == null || !BrowserInformationControl.isAvailable(shell))
1028 if (fCreator == null) {
1030 * FIXME: Take control creators (and link handling) out of JavadocHover,
1031 * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=232024
1033 JavadocHover.PresenterControlCreator presenterControlCreator= new JavadocHover.PresenterControlCreator(getSite());
1034 fCreator= new JavadocHover.HoverControlCreator(presenterControlCreator, true);
1039 private IWorkbenchSite getSite() {
1040 IWorkbenchPage page= JavaPlugin.getActivePage();
1042 IWorkbenchPart part= page.getActivePart();
1044 return part.getSite();
1049 public String getSortString() {
1053 protected void setSortString(String string) {
1054 fSortString= string;
1057 protected ITextViewer getTextViewer() {
1061 protected boolean isToggleEating() {
1062 return fToggleEating;
1066 * Sets up a simple linked mode at {@link #getCursorPosition()} and an exit policy that will
1067 * exit the mode when <code>closingCharacter</code> is typed and an exit position at
1068 * <code>getCursorPosition() + 1</code>.
1070 * @param document the document
1071 * @param closingCharacter the exit character
1073 protected void setUpLinkedMode(IDocument document, char closingCharacter) {
1074 if (getTextViewer() != null && autocloseBrackets()) {
1075 int offset= getReplacementOffset() + getCursorPosition();
1076 int exit= getReplacementOffset() + getReplacementString().length();
1078 LinkedPositionGroup group= new LinkedPositionGroup();
1079 group.addPosition(new LinkedPosition(document, offset, 0, LinkedPositionGroup.NO_STOP));
1081 LinkedModeModel model= new LinkedModeModel();
1082 model.addGroup(group);
1083 model.forceInstall();
1085 LinkedModeUI ui= new EditorLinkedModeUI(model, getTextViewer());
1086 ui.setSimpleMode(true);
1087 ui.setExitPolicy(new ExitPolicy(closingCharacter, document));
1088 ui.setExitPosition(getTextViewer(), exit, 0, Integer.MAX_VALUE);
1089 ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
1091 } catch (BadLocationException x) {
1097 protected boolean autocloseBrackets() {
1098 IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
1099 return preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS);
1102 protected void setDisplayString(String string) {
1103 fDisplayString= new StyledString(string);
1107 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension6#getStyledDisplayString()
1110 public StyledString getStyledDisplayString() {
1111 return fDisplayString;
1114 public void setStyledDisplayString(StyledString text) {
1115 fDisplayString= text;
1119 * @see java.lang.Object#toString()
1122 public String toString() {
1123 return getDisplayString();
1127 * Returns the java element proposed by the receiver, possibly <code>null</code>.
1129 * @return the java element proposed by the receiver, possibly <code>null</code>
1131 public IJavaElement getJavaElement() {
1132 if (getProposalInfo() != null)
1134 return getProposalInfo().getJavaElement();
1135 } catch (JavaModelException x) {
1142 * Tells whether required proposals are supported by this proposal.
1144 * @return <code>true</code> if required proposals are supported by this proposal
1145 * @see CompletionProposal#getRequiredProposals()
1148 protected boolean isSupportingRequiredProposals() {
1149 if (fInvocationContext == null)
1152 ProposalInfo proposalInfo= getProposalInfo();
1153 return proposalInfo.generated_5860057895832307195(this);