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();
}