Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] Re-submit: Big Huge CEditor Patch

Sorry about that, the first patch had some of the updated C/C++ Project
view cruft in it and Eclipse didn't mention it on the synchronize.

Here is the improved patch.  Note that this function changes the 
definition of IFunctionSummary which is used by extension points 
which contribute function hover help information.

---

Here is a big patch (sorry, I tried to keep it small, but one thing just
kept leading to another) which represents a new baseline for the code 
completion feature of the C/C++ Editor.  It has cleaned up some (but not
all) of the Java'isms in this area and now allows for two features,
proposal help information and argument hovers, not previously available.

  Next up will be the work to fill out the jump to definition, auto add 
include support, sticky context menus and the like.

Thanks,
  Thomas

CHANGELOG:
- 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.

Index: src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java,v
retrieving revision 1.4
diff -u -r1.4 DefaultCEditorTextHover.java
--- src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java	4 Feb 2003 20:00:46 -0000	1.4
+++ src/org/eclipse/cdt/internal/ui/editor/DefaultCEditorTextHover.java	24 Jun 2003 12:41:32 -0000
@@ -60,9 +60,14 @@
 
 			IFunctionSummary fs = CCompletionContributorManager.getDefault().getFunctionInfo(expression);
 			if(fs != null) {
-				buffer.append("<b>" + HTMLPrinter.convertToHTMLContent(expression) + 
-							  "()</b> - " + HTMLPrinter.convertToHTMLContent(fs.getSummary()) +
-							  "<br><br>" + HTMLPrinter.convertToHTMLContent(fs.getSynopsis()));
+				buffer.append("<b>");
+				buffer.append(HTMLPrinter.convertToHTMLContent(fs.getName()));
+				buffer.append("()</b>");
+				buffer.append(HTMLPrinter.convertToHTMLContent(fs.getPrototype().getPrototypeString(false)));
+				if(fs.getDescription() != null) {
+					buffer.append("<br><br>");
+					buffer.append(HTMLPrinter.convertToHTMLContent(fs.getDescription()));
+				}
 				int i;
 				for(i = 0; i < buffer.length(); i++) {
 					if(buffer.charAt(i) == '\\') {
Index: src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java,v
retrieving revision 1.7
diff -u -r1.7 CCompletionProcessor.java
--- src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java	13 Feb 2003 14:00:29 -0000	1.7
+++ src/org/eclipse/cdt/internal/ui/text/CCompletionProcessor.java	24 Jun 2003 12:41:32 -0000
@@ -20,6 +20,7 @@
 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.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 @@
 	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 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 @@
 	 * @see IContentAssistProcessor#getContextInformationValidator()
 	 */
 	public IContextInformationValidator getContextInformationValidator() {
-		return null;
+		if(fValidator == null) {
+			fValidator = new CParameterListValidator();
+		}
+		return fValidator;
 	}
 
 	/**
@@ -134,6 +141,13 @@
 	}
 
 	/**
+	 * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
+	 */
+	public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+		return null;
+	}
+
+	/**
 	 * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
 	 */
 	public char[] getCompletionProposalAutoActivationCharacters() {
@@ -151,13 +165,6 @@
 	}
 
 	/**
-	 * @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 @@
 		 * 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 @@
 		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--;
+				}
+				
+				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 e) {
+			} 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
+		if(region == null) {
+			return null;	//Bail out on error
+		}
+		
+		//@@@ TODO: Implement the structure member completion
+		if(isDereference) {
+			return null;
+		}
+				
 		try {
-			if (region != null) {
-				frag = document.get(region.getOffset(), region.getLength());
-				frag = frag.trim();
-				// No word is selected
-				if (frag.length() == 0) {
-					return null;
+			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;
 				}
-			} else {
-				return null;
 			}
-		} catch (BadLocationException x) {
-			// ignore
-			return null;
 		}
+		
 		// Based on the frag name, build a list of completion proposals
-		// We look in two places: the content outline and the libs
-
 		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 @@
 				}
 			} 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) {
Index: src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java,v
retrieving revision 1.1
diff -u -r1.1 CCompletionProposal.java
--- src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java	26 Jun 2002 20:55:44 -0000	1.1
+++ src/org/eclipse/cdt/internal/ui/text/CCompletionProposal.java	24 Jun 2003 12:41:33 -0000
@@ -7,22 +7,22 @@
 
 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 @@
 		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;
 	}
 	
 	/**
@@ -86,14 +93,6 @@
 	}
 	
 	/**
-	 * Sets the proposal info.
-	 * @param additionalProposalInfo The additional information associated with this proposal or <code>null</code>
-	 *
-	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)
 	 * @param cursorPosition The cursorPosition to set
@@ -146,22 +145,49 @@
 			}
 		} */
 	}
+
+	/*
+	 * @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 @@
 				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 @@
 		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 @@
 		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 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.
@@ -302,28 +354,6 @@
 	 */
 	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;
 	}
 	
 	/**
Index: src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java
===================================================================
RCS file: src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java
diff -N src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/cdt/internal/ui/text/CParameterListValidator.java	24 Jun 2003 12:41:33 -0000
@@ -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;
+	}
+}
+
Index: src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java,v
retrieving revision 1.10
diff -u -r1.10 CSourceViewerConfiguration.java
--- src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java	29 Jan 2003 18:18:30 -0000	1.10
+++ src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java	24 Jun 2003 12:41:33 -0000
@@ -26,6 +26,7 @@
 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 @@
 		reconciler.setDamager(dr, CPartitionScanner.C_MULTILINE_COMMENT);
 		reconciler.setRepairer(dr, CPartitionScanner.C_MULTILINE_COMMENT);
 
-
 		return reconciler;
 	}
 
@@ -167,15 +167,30 @@
 	 * @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 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);
 			}
Index: src/org/eclipse/cdt/internal/ui/text/CWordFinder.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java,v
retrieving revision 1.1
diff -u -r1.1 CWordFinder.java
--- src/org/eclipse/cdt/internal/ui/text/CWordFinder.java	26 Jun 2002 20:55:44 -0000	1.1
+++ src/org/eclipse/cdt/internal/ui/text/CWordFinder.java	24 Jun 2003 12:41:33 -0000
@@ -10,9 +10,31 @@
 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 @@
 		
 		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;
+	}
+	
 }
 
 
Index: src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java
===================================================================
RCS file: src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java
diff -N src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/cdt/ui/FunctionPrototypeSummary.java	24 Jun 2003 12:41:33 -0000
@@ -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();
+	}
+}
Index: src/org/eclipse/cdt/ui/IFunctionSummary.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/IFunctionSummary.java,v
retrieving revision 1.1
diff -u -r1.1 IFunctionSummary.java
--- src/org/eclipse/cdt/ui/IFunctionSummary.java	26 Jun 2002 20:55:44 -0000	1.1
+++ src/org/eclipse/cdt/ui/IFunctionSummary.java	24 Jun 2003 12:41:33 -0000
@@ -7,28 +7,80 @@
 
 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 function summary
+	 * 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 getSummary();
-	
+	public String getNamespace();
+
 	/**
-	 * Get the function prototype
+	 * 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 getPrototype();
+	public String getDescription();
 	
 	/**
-	 * Get the function synopsis
+	 * Gets the prototype description for this function. 
+	 * @return The IFunctionPrototypeSummary describing the 
+	 * prototype for this function 
 	 */
-	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();
 }

Back to the top