diff --git a/core/org.eclipse.cdt.ui/ChangeLog b/core/org.eclipse.cdt.ui/ChangeLog index ac3fb21cd52..4b299a98fa3 100644 --- a/core/org.eclipse.cdt.ui/ChangeLog +++ b/core/org.eclipse.cdt.ui/ChangeLog @@ -1,3 +1,21 @@ +2003-06-24 Thomas Fletcher + + - Proposals will now include additional help information with them + if it is available (same as JDT). This opens the door for being + able to write a Javadoc/Doxygen parser and integrating live, + context specific, help. + - On function completions a hover is now shown above the function + (same as JDT) with the argument information as it is being filled in. + + * src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java + * src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java + * src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java + * src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java + * src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java + * src/org/eclipse/cdt/internal/ui/text/CWordFinder.java + * src/org/eclipse/cdt/ui/IFunctionSummary.java + * src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java + 2003-06-23 John Camelon Updated Factory infrastructure, constructors, etc. Introduced Preprocessor class for transitive closure calc. client. 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 940b55c26ef..b04b7c5ef0e 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 @@ -60,9 +60,14 @@ public class DefaultCEditorTextHover implements ITextHover IFunctionSummary fs = CCompletionContributorManager.getDefault().getFunctionInfo(expression); if(fs != null) { - buffer.append("" + HTMLPrinter.convertToHTMLContent(expression) + - "() - " + HTMLPrinter.convertToHTMLContent(fs.getSummary()) + - "

" + HTMLPrinter.convertToHTMLContent(fs.getSynopsis())); + buffer.append(""); + buffer.append(HTMLPrinter.convertToHTMLContent(fs.getName())); + buffer.append("()"); + buffer.append(HTMLPrinter.convertToHTMLContent(fs.getPrototype().getPrototypeString(false))); + if(fs.getDescription() != null) { + buffer.append("

"); + buffer.append(HTMLPrinter.convertToHTMLContent(fs.getDescription())); + } int i; for(i = 0; i < buffer.length(); i++) { if(buffer.charAt(i) == '\\') { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java index a4e3963d6a8..90b25a0c416 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java @@ -20,6 +20,7 @@ import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.text.template.TemplateEngine; import org.eclipse.cdt.ui.CElementLabelProvider; import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.FunctionPrototypeSummary; import org.eclipse.cdt.ui.IFunctionSummary; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -28,6 +29,7 @@ import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ContextInformation; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; @@ -46,8 +48,10 @@ public class CCompletionProcessor implements IContentAssistProcessor { private CEditor fEditor; private char[] fProposalAutoActivationSet; private CCompletionProposalComparator fComparator; - private TemplateEngine[] fTemplateEngine; + private IContextInformationValidator fValidator; + private TemplateEngine[] fTemplateEngine; + private boolean fRestrictToMatchingCase; private boolean fAllowAddIncludes; @@ -56,7 +60,7 @@ public class CCompletionProcessor implements IContentAssistProcessor { public CCompletionProcessor(IEditorPart editor) { fEditor = (CEditor) editor; - + //Determine if this is a C or a C++ file for the context completion + //This is _totally_ ugly and likely belongs in the main editor class. String contextNames[] = new String[2]; ArrayList templateList = new ArrayList(2); @@ -123,7 +127,10 @@ public class CCompletionProcessor implements IContentAssistProcessor { * @see IContentAssistProcessor#getContextInformationValidator() */ public IContextInformationValidator getContextInformationValidator() { - return null; + if(fValidator == null) { + fValidator = new CParameterListValidator(); + } + return fValidator; } /** @@ -133,6 +140,13 @@ public class CCompletionProcessor implements IContentAssistProcessor { return null; } + /** + * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int) + */ + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + return null; + } + /** * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() */ @@ -150,13 +164,6 @@ public class CCompletionProcessor implements IContentAssistProcessor { fProposalAutoActivationSet = activationSet; } - /** - * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int) - */ - public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { - return null; - } - /** * Tells this processor to restrict is proposals to those * starting with matching cases. @@ -238,14 +245,7 @@ public class CCompletionProcessor implements IContentAssistProcessor { * applies to all proposals and not just those of the compilation unit. */ order(results); - if ((results.length == 1) - && (CUIPlugin.getDefault().getPreferenceStore().getBoolean(ContentAssistPreference.AUTOINSERT))) { - results[0].apply(document); - // Trick the content assistant into thinking we have no proposals - return new ICCompletionProposal[0]; - } else { - return results; - } + return results; } /** @@ -256,102 +256,140 @@ public class CCompletionProcessor implements IContentAssistProcessor { return proposals; } - private void addProjectCompletions(IProject project, IRegion region, String frag, ArrayList completions) { - IndexModel model = IndexModel.getDefault(); - - ITagEntry[] tags = model.query(project, frag + "*", false, false); - if (tags != null && tags.length > 0) { - // We have some matches! - for (int i = 0; i < tags.length; i++) { - - String fname = tags[i].getTagName(); - - int kind = tags[i].getKind(); - - if (kind == TagFlags.T_FUNCTION || kind == TagFlags.T_PROTOTYPE) { - fname = fname + "()"; - } - String proto = fname + " - " + tags[i].getPattern(); - //System.out.println("tagmatch " + fname + " proto " + proto + " type" + tags[i].getKind()); - if (tags[i].getKind() != TagFlags.T_MEMBER) { - completions.add(new CCompletionProposal(fname, region.getOffset(), region.getLength(), - //fname.length() + 1, - getTagImage(kind), proto.equals("") ? (fname + "()") : proto, - //null, - //null)); - 3)); - } - } - } - } - /** * Evaluate the actual proposals for C */ private ICCompletionProposal[] evalProposals(IDocument document, int pos, int length) { - IRegion region; + boolean isDereference = false; + IRegion region; String frag = ""; - + // Move back the pos by one the position is 0-based if (pos > 0) { pos--; } - // First, check if we're on a space or trying to open a struct/union + // TODO: Check to see if we are trying to open for a structure/class, then + // provide that structure's completion instead of the function/variable + // completions. This needs to be properly dealt with so that we can + // offer completion proposals. if (pos > 1) { + int struct_pos = pos; + try { - // If we're on a space and the previous character is valid text, - // parse the previous word. - if (!Character.isJavaIdentifierPart(document.getChar(pos))) { - pos--; - if (!Character.isJavaIdentifierPart(document.getChar(pos))) { - pos--; - // Comment out the dereference code, only useful once we can - // know variable types to go fish back structure members - //if (document.getChar(offset) == '.') { - // isDereference = true; - // offset--; - //} else if ((document.getChar(offset) == '>') && (document.getChar(offset - 1) == '-')) { - // isDereference = true; - // offset -= 2; - //} - } + //While we aren't on a space, then go back and look for + // . or a -> then determine the structure variable type. + while(document.getChar(struct_pos) == ' ') { + struct_pos--; } - } catch (BadLocationException e) { + + if (document.getChar(struct_pos) == '.') { + isDereference = true; + pos -= struct_pos - 1; + } else if ((document.getChar(struct_pos) == '>') && (document.getChar(struct_pos - 1) == '-')) { + isDereference = true; + pos -= struct_pos - 2; + } else { + isDereference = false; + } + } catch (BadLocationException ex) { return null; } } - // Get the current "word" + // Get the current "word", it might be a variable or another starter region = CWordFinder.findWord(document, pos); - - // If we're currently - try { - if (region != null) { - frag = document.get(region.getOffset(), region.getLength()); - frag = frag.trim(); - // No word is selected - if (frag.length() == 0) { - return null; - } - } else { - return null; - } - } catch (BadLocationException x) { - // ignore + if(region == null) { + return null; //Bail out on error + } + + //@@@ TODO: Implement the structure member completion + if(isDereference) { return null; } - // Based on the frag name, build a list of completion proposals - // We look in two places: the content outline and the libs + + try { + frag = document.get(region.getOffset(), region.getLength()); + frag = frag.trim(); + } catch (BadLocationException ex) { + return null; //Bail out on error + } + + //If there is no fragment, then see if we are in a function + if(frag.length() == 0) { + IRegion funcregion; + String funcfrag = ""; + funcregion = CWordFinder.findFunction(document, pos + 1); + if(funcregion != null) { + try { + funcfrag = document.get(funcregion.getOffset(), funcregion.getLength()); + funcfrag = funcfrag.trim(); + } catch(Exception ex) { + funcfrag = ""; + } + if(funcfrag.length() == 0) { + return null; + } else { + //@@@ Add some marker here to indicate different path! + region = funcregion; + frag = funcfrag; + } + } + } + + // Based on the frag name, build a list of completion proposals ArrayList completions = new ArrayList(); // Look in index manager + addProposalsFromModel(region, frag, completions); + + // Loot in the contributed completions + addProposalsFromCompletionContributors(region, frag, completions); + + return (ICCompletionProposal[]) completions.toArray(new ICCompletionProposal[0]); + } + + private void addProposalsFromCompletionContributors(IRegion region, String frag, ArrayList completions) { + IFunctionSummary[] summary; + + summary = CCompletionContributorManager.getDefault().getMatchingFunctions(frag); + if(summary == null) { + return; + } + + for (int i = 0; i < summary.length; i++) { + String fname = summary[i].getName() + "()"; + String fdesc = summary[i].getDescription(); + IFunctionSummary.IFunctionPrototypeSummary fproto = summary[i].getPrototype(); + String fargs = fproto.getArguments(); + + CCompletionProposal proposal; + proposal = new CCompletionProposal(fname, + region.getOffset(), + region.getLength(), + getTagImage(TagFlags.T_FUNCTION), + fproto.getPrototypeString(true), + 2); + + if(fdesc != null) { + proposal.setAdditionalProposalInfo(fdesc); + } + + if(fargs != null && fargs.length() > 0) { + proposal.setContextInformation(new ContextInformation(fname, fargs)); + } + + completions.add(proposal); + } + } + + private void addProposalsFromModel(IRegion region, String frag, ArrayList completions) { IProject project = null; IEditorInput input = fEditor.getEditorInput(); if (input instanceof IFileEditorInput) { project = ((IFileEditorInput) input).getFile().getProject(); - + // Bail out quickly, if the project was deleted. if (!project.exists()) { project = null; @@ -370,26 +408,54 @@ public class CCompletionProcessor implements IContentAssistProcessor { } } catch (CoreException e) { } - } + } - IFunctionSummary[] summary; + private void addProjectCompletions(IProject project, IRegion region, String frag, ArrayList completions) { + IndexModel model = IndexModel.getDefault(); - //UserHelpFunctionInfo inf = plugin.getFunctionInfo(); - summary = CCompletionContributorManager.getDefault().getMatchingFunctions(frag); - if (summary != null) { - for (int i = 0; i < summary.length; i++) { - String fname = summary[i].getName(); - String proto = summary[i].getPrototype(); - completions.add(new CCompletionProposal(fname + "()", region.getOffset(), region.getLength(), - //fname.length() + 1, - CPluginImages.get(CPluginImages.IMG_OBJS_FUNCTION), proto.equals("") ? (fname + "()") : proto, - //null, - //null)); - 2)); + ITagEntry[] tags = model.query(project, frag + "*", false, false); + if (tags != null && tags.length > 0) { + for (int i = 0; i < tags.length; i++) { + String fname = tags[i].getTagName(); + FunctionPrototypeSummary fproto = null; + int kind = tags[i].getKind(); + + if (kind == TagFlags.T_FUNCTION || kind == TagFlags.T_PROTOTYPE) { + fname = fname + "()"; + } + + if(tags[i].getPattern() != null) { + try { + fproto = new FunctionPrototypeSummary(tags[i].getPattern()); + } catch(Exception ex) { + fproto = null; + } + } + if(fproto == null) { + fproto = new FunctionPrototypeSummary(fname); + } + + //System.out.println("tagmatch " + fname + " proto " + proto + " type" + tags[i].getKind()); + if (kind != TagFlags.T_MEMBER) { + CCompletionProposal proposal; + proposal = new CCompletionProposal(fname, + region.getOffset(), + region.getLength(), + getTagImage(kind), + fproto.getPrototypeString(true), + 3); + completions.add(proposal); + + //No summary information available yet + + String fargs = fproto.getArguments(); + if(fargs != null && fargs.length() > 0) { + proposal.setContextInformation(new ContextInformation(fname, fargs)); + } + } } } - return (ICCompletionProposal[]) completions.toArray(new ICCompletionProposal[0]); } private Image getTagImage(int kind) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java index 82a8705851b..5a2c8279562 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java @@ -7,22 +7,22 @@ package org.eclipse.cdt.internal.ui.text; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.util.Assert; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; - -public class CCompletionProposal implements ICCompletionProposal { +public class CCompletionProposal implements ICCompletionProposal, ICompletionProposalExtension { private String fDisplayString; private String fReplacementString; + private String fAdditionalInfoString; private int fReplacementOffset; private int fReplacementLength; private int fCursorPosition; private Image fImage; private IContextInformation fContextInformation; private int fContextInformationPosition; - //private ProposalInfo fProposalInfo; //private IImportDeclaration fImportDeclaration; private char[] fTriggerCharacters; @@ -50,13 +50,20 @@ public class CCompletionProposal implements ICCompletionProposal { fDisplayString= displayString != null ? displayString : replacementString; fRelevance= relevance; - fCursorPosition= replacementString.length(); + //@@@ Is this the best way to do this, likely it isn't + if(replacementString.indexOf("()") == -1) { //Not replacing with a function + fCursorPosition = replacementString.length(); + } else if(displayString.indexOf("()") == -1) { //Assume that there are arguments between () + fCursorPosition = replacementString.length() - 1; + } else { + fCursorPosition = replacementString.length(); + } + fAdditionalInfoString = null; fContextInformation= null; fContextInformationPosition= -1; //fIncludeDeclaration= null; fTriggerCharacters= null; - //fProposalInfo= null; } /** @@ -85,14 +92,6 @@ public class CCompletionProposal implements ICCompletionProposal { fTriggerCharacters= triggerCharacters; } - /** - * Sets the proposal info. - * @param additionalProposalInfo The additional information associated with this proposal or null - * - public void setProposalInfo(ProposalInfo proposalInfo) { - fProposalInfo= proposalInfo; - }*/ - /** * Sets the cursor position relative to the insertion offset. By default this is the length of the completion string * (Cursor positioned after the completion) @@ -146,22 +145,49 @@ public class CCompletionProposal implements ICCompletionProposal { } } */ } + + /* + * @see ICompletionProposal#apply + */ + public void apply(IDocument document) { + apply(document, (char) 0, fReplacementOffset + fReplacementLength); + } /* + * In this case we need to apply the completion proposal intelligently. + * This means that if we are applying it to a function, we don't wipe + * out the internal arguments, and if the proposal is a function, and it + * already is bracketed, then don't put those brackets in. + * * @see ICompletionProposalExtension#apply(IDocument, char, int) */ public void apply(IDocument document, char trigger, int offset) { + int functionBracketIndex; + boolean isBeforeBracket; + String replacementStringCopy = fReplacementString; + + //If just providing context information, then don't move the cursor + if(offset != (fReplacementOffset + fReplacementLength)) { + fCursorPosition = offset - fReplacementOffset; + } + try { + functionBracketIndex = fReplacementString.indexOf("()"); + isBeforeBracket = document.getChar(fReplacementOffset + fReplacementLength) == '('; - // patch replacement length - int delta= offset - (fReplacementOffset + fReplacementLength); - if (delta > 0) - fReplacementLength += delta; - + //Strip the brackets off the function if inserting right before brackets + if(functionBracketIndex != -1 && isBeforeBracket) { + replacementStringCopy = fReplacementString.substring(0, functionBracketIndex); + } + } catch(Exception ex) { + /* Ignore */ + } + + try { if (trigger == (char) 0) { - replace(document, fReplacementOffset, fReplacementLength, fReplacementString); + replace(document, fReplacementOffset, fReplacementLength, replacementStringCopy); } else { - StringBuffer buffer= new StringBuffer(fReplacementString); + StringBuffer buffer= new StringBuffer(replacementStringCopy); // fix for PR #5533. Assumes that no eating takes place. if ((fCursorPosition > 0 && fCursorPosition <= buffer.length() && buffer.charAt(fCursorPosition - 1) != trigger)) { @@ -172,6 +198,12 @@ public class CCompletionProposal implements ICCompletionProposal { replace(document, fReplacementOffset, fReplacementLength, buffer.toString()); } + /* + * The replacement length is used to calculate the new cursor position, + * so after we update the includes adjust the replacement offset. + * NOTE: This won't work if the include is added after the offset, + * such as might be the case with #include completions. + */ int oldLen= document.getLength(); applyIncludes(document); fReplacementOffset += document.getLength() - oldLen; @@ -186,13 +218,6 @@ public class CCompletionProposal implements ICCompletionProposal { if (!document.get(offset, length).equals(string)) document.replace(offset, length, string); } - - /* - * @see ICompletionProposal#apply - */ - public void apply(IDocument document) { - apply(document, (char) 0, fReplacementOffset + fReplacementLength); - } /* * @see ICompletionProposal#getSelection @@ -222,14 +247,20 @@ public class CCompletionProposal implements ICCompletionProposal { return fDisplayString; } + /** + * Set the additional information which will be shown when this + * proposal is selected in the popup list. + * @param infoString + */ + public void setAdditionalProposalInfo(String infoString) { + fAdditionalInfoString = infoString; + } + /* * @see ICompletionProposal#getAdditionalProposalInfo() */ public String getAdditionalProposalInfo() { - //if (fProposalInfo != null) { - // return fProposalInfo.getInfo(); - //} - return null; + return fAdditionalInfoString; } /* @@ -245,6 +276,27 @@ public class CCompletionProposal implements ICCompletionProposal { public int getContextInformationPosition() { return fReplacementOffset + fContextInformationPosition; } + + /* + * @see ICompletionProposalExtension#isValidFor(IDocument, int) + */ + public boolean isValidFor(IDocument document, int offset) { + if (offset < fReplacementOffset) + return false; + + int replacementLength= fReplacementString == null ? 0 : fReplacementString.length(); + if (offset >= fReplacementOffset + replacementLength) + return false; + + try { + int length= offset - fReplacementOffset; + String start= document.get(fReplacementOffset, length); + return fReplacementString.substring(0, length).equalsIgnoreCase(start); + } catch (BadLocationException x) { + } + + return false; + } /** * Gets the replacement offset. @@ -303,28 +355,6 @@ public class CCompletionProposal implements ICCompletionProposal { public void setImage(Image image) { fImage= image; } - - /* - * @see ICompletionProposalExtension#isValidFor(IDocument, int) - */ - public boolean isValidFor(IDocument document, int offset) { - - if (offset < fReplacementOffset) - return false; - - int replacementLength= fReplacementString == null ? 0 : fReplacementString.length(); - if (offset >= fReplacementOffset + replacementLength) - return false; - - try { - int length= offset - fReplacementOffset; - String start= document.get(fReplacementOffset, length); - return fReplacementString.substring(0, length).equalsIgnoreCase(start); - } catch (BadLocationException x) { - } - - return false; - } /** * Gets the proposal's relevance. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java new file mode 100644 index 00000000000..dc37c219d0b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java @@ -0,0 +1,216 @@ +package org.eclipse.cdt.internal.ui.text; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; + +import org.eclipse.jface.text.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationPresenter; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; + +/** + * This class provides the function parameter parsing for the C/C++ Editor hover + * It is based heavily on the Java class JavaParameterListValidator + * + * @author thomasf + * + */ +public class CParameterListValidator implements IContextInformationValidator, IContextInformationPresenter { + private int fPosition; + private ITextViewer fViewer; + private IContextInformation fInformation; + + private int fCurrentParameter; + + public CParameterListValidator() { + } + + /** + * @see IContextInformationValidator#install(IContextInformation, ITextViewer, int) + * @see IContextInformationPresenter#install(IContextInformation, ITextViewer, int) + */ + public void install(IContextInformation info, ITextViewer viewer, int documentPosition) { + + fPosition= documentPosition; + fViewer= viewer; + fInformation= info; + + fCurrentParameter= -1; + } + + private int getCommentEnd(IDocument d, int pos, int end) throws BadLocationException { + while (pos < end) { + char curr= d.getChar(pos); + pos++; + if (curr == '*') { + if (pos < end && d.getChar(pos) == '/') { + return pos + 1; + } + } + } + return end; + } + + private int getStringEnd(IDocument d, int pos, int end, char ch) throws BadLocationException { + while (pos < end) { + char curr= d.getChar(pos); + pos++; + if (curr == '\\') { + // ignore escaped characters + pos++; + } else if (curr == ch) { + return pos; + } + } + return end; + } + + private int getCharCount(IDocument document, int start, int end, + char increment, char decrement, boolean considerNesting) throws BadLocationException { + + Assert.isTrue((increment != 0 || decrement != 0) && increment != decrement); + + int nestingLevel= 0; + int charCount= 0; + while (start < end) { + char curr= document.getChar(start++); + switch (curr) { + case '/': + if (start < end) { + char next= document.getChar(start); + if (next == '*') { + // a comment starts, advance to the comment end + start= getCommentEnd(document, start + 1, end); + } else if (next == '/') { + // '//'-comment: nothing to do anymore on this line + start= end; + } + } + break; + case '*': + if (start < end) { + char next= document.getChar(start); + if (next == '/') { + // we have been in a comment: forget what we read before + charCount= 0; + ++ start; + } + } + break; + case '"': + case '\'': + start= getStringEnd(document, start, end, curr); + break; + default: + + if (considerNesting) { + + if ('(' == curr) + ++ nestingLevel; + else if (')' == curr) + -- nestingLevel; + + if (nestingLevel != 0) + break; + } + + if (increment != 0) { + if (curr == increment) + ++ charCount; + } + + if (decrement != 0) { + if (curr == decrement) + -- charCount; + } + } + } + + return charCount; + } + + /** + * @see IContextInformationValidator#isContextInformationValid(int) + */ + public boolean isContextInformationValid(int position) { + + try { + if (position < fPosition) + return false; + + IDocument document= fViewer.getDocument(); + IRegion line= document.getLineInformationOfOffset(fPosition); + + if (position > line.getOffset() + line.getLength()) + return false; + + return (getCharCount(document, fPosition, position, '(', ')', false) >= 0); + + } catch (BadLocationException x) { + return false; + } + } + + /** + * @see IContextInformationPresenter#updatePresentation(int, TextPresentation) + */ + public boolean updatePresentation(int position, TextPresentation presentation) { + + int currentParameter= -1; + + try { + currentParameter= getCharCount(fViewer.getDocument(), fPosition, position, ',', (char) 0, true); + } catch (BadLocationException x) { + return false; + } + + if (fCurrentParameter != -1) { + if (currentParameter == fCurrentParameter) + return false; + } + + presentation.clear(); + fCurrentParameter= currentParameter; + + String s= fInformation.getInformationDisplayString().trim(); + + int start= 0; + int occurrences= 0; + while (occurrences < fCurrentParameter) { + int found= s.indexOf(',', start); + if (found == -1) + break; + start= found + 1; + ++ occurrences; + } + + if (occurrences < fCurrentParameter) { + presentation.addStyleRange(new StyleRange(0, s.length(), null, null, SWT.NORMAL)); + return true; + } + + if (start == -1) + start= 0; + + int end= s.indexOf(',', start); + if (end == -1) + end= s.length(); + + if (start > 0) + presentation.addStyleRange(new StyleRange(0, start, null, null, SWT.NORMAL)); + + if (end > start) + presentation.addStyleRange(new StyleRange(start, end - start, null, null, SWT.BOLD)); + + if (end < s.length()) + presentation.addStyleRange(new StyleRange(end, s.length() - end, null, null, SWT.NORMAL)); + + return true; + } +} + 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 ba6e15b32ac..cccdceea30a 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 @@ -26,6 +26,7 @@ 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; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.formatter.ContentFormatter; import org.eclipse.jface.text.formatter.IContentFormatter; @@ -158,7 +159,6 @@ public class CSourceViewerConfiguration extends SourceViewerConfiguration { reconciler.setDamager(dr, CPartitionScanner.C_MULTILINE_COMMENT); reconciler.setRepairer(dr, CPartitionScanner.C_MULTILINE_COMMENT); - return reconciler; } @@ -167,15 +167,30 @@ public class CSourceViewerConfiguration extends SourceViewerConfiguration { * @see SourceViewerConfiguration#getContentAssistant(ISourceViewer) */ public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { + if(getEditor() == null) { + return null; + } + ContentAssistant assistant = new ContentAssistant(); - // IFile file = (sourceViewer). - assistant.setContentAssistProcessor(new CCompletionProcessor(fEditor), IDocument.DEFAULT_CONTENT_TYPE); + + IContentAssistProcessor processor = new CCompletionProcessor(getEditor()); + assistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE); + + //Will this work as a replacement for the configuration lines below? + //ContentAssistPreference.configure(assistant, getPreferenceStore()); + + assistant.enableAutoInsert(CUIPlugin.getDefault().getPreferenceStore().getBoolean(ContentAssistPreference.AUTOINSERT)); assistant.enableAutoActivation(true); assistant.setAutoActivationDelay(500); assistant.setProposalPopupOrientation(IContentAssistant.PROPOSAL_OVERLAY); + + assistant.setContextInformationPopupOrientation(ContentAssistant.CONTEXT_INFO_ABOVE); + assistant.setInformationControlCreator(getInformationControlCreator(sourceViewer)); + return assistant; } + /** * @see SourceViewerConfiguration#getReconciler(ISourceViewer) */ @@ -367,7 +382,7 @@ public class CSourceViewerConfiguration extends SourceViewerConfiguration { 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); + int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL); return new DefaultInformationControl(parent, style, new HTMLTextPresenter(cutDown)); // return new HoverBrowserControl(parent); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java index 8409c45eb0e..fb1d4243836 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java @@ -10,9 +10,31 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; - +/** + * This is a helper class for the text editor to be able to determine, + * given a particular offset in a document, various candidates segments + * for things like context help, proposals and hovering. + */ public class CWordFinder { + /** + * This method determines for a given offset into a given document + * what the region is which defines the current word. A word is + * defined as the set of non "C" identifiers. So assuming that ! + * indicated the current cursor postion: + * !afunction(int a, int b) --> word = length 0 + * afunc!tion(int a, int b) --> word = afunction + * afunction!(int a, int b) --> word = afunction + * afunction(!int a, int b) --> word = length 0 + * afunction(int a,! int b) --> word = length 0 + * afunction(!) --> word = length 0 + * @param document The document to be examined + * @param offset The offset into the document where a word should + * be idendified. + * @return The region defining the current word, which may be a + * region of length 0 if the offset is not in a word, or null if + * there is an error accessing the docment data. + */ public static IRegion findWord( IDocument document, int offset ) { int start = -1; @@ -63,6 +85,121 @@ public class CWordFinder return null; } + + /** + * This method will determine the region for the name of the function + * within which the current offset is contained. + * @param document The document to be examined + * @param offset The offset into the document where a word should + * be idendified. + * @return The region defining the current word, which may be a + * region of length 0 if the offset is not in a function, or null if + * there is an error accessing the docment data. + */ + public static IRegion findFunction( IDocument document, int offset ) + { + int leftbracket = -1; + int leftbracketcount = 0; + int rightbracket = -1; + int rightbracketcount = 0; + int functionstart = -1; + int functionend = -1; + + + try + { + int length = document.getLength(); + int pos; + char c; + + //Find most relevant right bracket from our position + pos = offset; + rightbracketcount = leftbracketcount = 0; + while(pos < length) { + c = document.getChar( pos ); + + if( c == ')') { + rightbracketcount++; + if(rightbracketcount >= leftbracketcount) { + rightbracket = pos; + break; + } + } + + if(c == '(') { + leftbracketcount++; + } + + if(c == ';') { + break; + } + + pos++; + } + + if ( rightbracket == -1 ) { + return new Region(offset, 0); + } + + //Now backtrack our way from the rightbracket to the left + pos = rightbracket; + rightbracketcount = leftbracketcount = 0; + while(pos >= 0) { + c = document.getChar( pos ); + + if( c == ')') { + rightbracketcount++; + } + + if(c == '(') { + leftbracketcount++; + if(leftbracketcount >= rightbracketcount) { + leftbracket = pos; + break; + } + } + + if(c == ';') { + break; + } + + pos--; + } + + if ( leftbracket == -1 ) { + return new Region(offset, 0); + } + + //Now work our way to the function name + pos = leftbracket - 1; + while(pos >= 0) { + c = document.getChar( pos ); + if(functionend == -1 && c == ' ' ) { + continue; + } + + if(!Character.isJavaIdentifierPart(c)) { + break; + } + + functionstart = pos; + if(functionend == -1) { + functionend = pos; + } + + pos--; + } + } catch( BadLocationException x ) { + /* Ignore */ + } + + if (functionstart > -1 && functionend > -1) { + return new Region(functionstart, functionend - functionstart + 1); + } + + return null; + } + } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java new file mode 100644 index 00000000000..34d6e81dfd6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java @@ -0,0 +1,69 @@ +package org.eclipse.cdt.ui; + +/** + * This class is a helper class which takes care of implementing some of the + * function prototype parsing and stripping. + */ +public class FunctionPrototypeSummary implements IFunctionSummary.IFunctionPrototypeSummary { + String fname; + String freturn; + String farguments; + + /** + * Creates a prototype which matches the format + * returntype function(arguments) + * @param properProto + */ + public FunctionPrototypeSummary(String proto) { + int leftbracket = proto.indexOf('('); + int rightbracket = proto.lastIndexOf(')'); + farguments = proto.substring(leftbracket + 1, rightbracket); + + int nameend = leftbracket - 1; + while(proto.charAt(nameend) == ' ') { + nameend--; + } + int namestart = nameend; + while(namestart > 0 && proto.charAt(namestart) != ' ') { + namestart--; + } + fname = proto.substring(namestart, nameend + 1).trim(); + + if(namestart == 0) { + freturn = "void"; + } else { + freturn = proto.substring(0, namestart).trim(); + } + } + + public String getName() { + return fname; + } + + public String getReturnType() { + return freturn; + } + + public String getArguments() { + return farguments; + } + + public String getPrototypeString(boolean namefirst) { + StringBuffer buffer = new StringBuffer(); + if(!namefirst) { + buffer.append(getArguments()); + buffer.append(" "); + } + buffer.append(getName()); + buffer.append("("); + if(getArguments() != null) { + buffer.append(getArguments()); + } + buffer.append(")"); + if(namefirst) { + buffer.append(" "); + buffer.append(getReturnType()); + } + return buffer.toString(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/IFunctionSummary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/IFunctionSummary.java index b5adc426bfd..403860cd5d0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/IFunctionSummary.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/IFunctionSummary.java @@ -7,28 +7,80 @@ package org.eclipse.cdt.ui; public interface IFunctionSummary { + public interface IFunctionPrototypeSummary { + /** + * Get the name of the function. This should be the + * same as for IFunctionSummary. + * ie "int main(int argc, char **argv)" --> "main" + * @return The name of the function without any additional + * information. + */ + public String getName(); + + /** + * Get the return type of the function. + * ie "int main(int argc, char **argv)" --> "int" + * @return A string containing the return type of the + * function. + */ + public String getReturnType(); + + /** + * Get the arguments of the function. + * ie "int main(int argc, char **argv)" --> "int argc, char **argv" + * @return A string containing the arguments of the + * function, or null if the function has no arguments. + */ + public String getArguments(); + + /** + * Get a nice user defined string. The format of + * which depends on the variable namefirst + * namefirst == true: main(int argc, char **argv) int + * namefirst == false: int main(int argc, char **argv); + * @return + */ + public String getPrototypeString(boolean namefirst); + } + /** - * Get the name of the function + * Gets the name of the function. This is the simple + * name without any additional return or argument information. + * The function "int main(int argc, char **argv)" would + * return "main" + * @return The name of the function without any additional + * information */ public String getName(); + + /** + * Get the full namespace qualifier for this function + * (generally C++ only) + * @return The string of the fully qualified namespace for + * this function, or null if the namespace is not known. + */ + public String getNamespace(); + + /** + * Gets the description of the function. This string can be + * either text or HTML coded and is displayed as part of the + * hover help and as the context proposal information. + * @return A description for this function, or null if no + * description is available. + */ + public String getDescription(); /** - * Get the function summary + * Gets the prototype description for this function. + * @return The IFunctionPrototypeSummary describing the + * prototype for this function */ - public String getSummary(); - - /** - * Get the function prototype - */ - public String getPrototype(); - - /** - * Get the function synopsis - */ - public String getSynopsis(); - + public IFunctionPrototypeSummary getPrototype(); + /** * Get headers required by this function + * @return A list of IRequiredInclude definitions, or null if no + * include definitions are available. */ public IRequiredInclude[] getIncludes(); }