blob: 77e3d466a717a142259cc13f681bd74352863d8e [file] [log] [blame]
package org.eclipse.cdt.internal.ui.text.contentassist;
/*
* (c) Copyright QNX Software Systems Ltd. 2002.
* All Rights Reserved.
*/
import org.eclipse.cdt.internal.ui.text.link.LinkedPositionManager;
import org.eclipse.cdt.internal.ui.text.link.LinkedPositionUI;
import org.eclipse.cdt.internal.ui.text.link.LinkedPositionUI.ExitFlags;
import org.eclipse.cdt.ui.text.ICCompletionProposal;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.util.Assert;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
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 IImportDeclaration fImportDeclaration;
private char[] fTriggerCharacters;
protected ITextViewer fTextViewer;
private int fRelevance;
/**
* Creates a new completion proposal. All fields are initialized based on the provided information.
*
* @param replacementString the actual string to be inserted into the document
* @param replacementOffset the offset of the text to be replaced
* @param replacementLength the length of the text to be replaced
* @param image the image to display for this proposal
* @param displayString the string to be displayed for the proposal
* If set to <code>null</code>, the replacement string will be taken as display string.
*/
public CCompletionProposal(String replacementString, int replacementOffset, int replacementLength, Image image, String displayString, int relevance, ITextViewer viewer) {
Assert.isNotNull(replacementString);
Assert.isTrue(replacementOffset >= 0);
Assert.isTrue(replacementLength >= 0);
fReplacementString= replacementString;
fReplacementOffset= replacementOffset;
fReplacementLength= replacementLength;
fImage= image;
fDisplayString= displayString != null ? displayString : replacementString;
fRelevance= relevance;
fTextViewer= viewer;
//@@@ 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;
}
/**
* Sets the context information.
* @param contentInformation The context information associated with this proposal
*/
public void setContextInformation(IContextInformation contextInformation) {
fContextInformation= contextInformation;
fContextInformationPosition= (fContextInformation != null ? fCursorPosition : -1);
}
/**
* Sets the import declaration to import when applied.
* @param importDeclaration Optional import declaration to be added. Can be <code>null</code>. The underlying compilation unit
* is assumed to be compatible with the document passed in <code>apply</code>.
*
public void setIncludeDeclaration(IImportDeclaration importDeclaration) {
fIncludeDeclaration= importDeclaration;
} */
/**
* Sets the trigger characters.
* @param triggerCharacters The set of characters which can trigger the application of this completion proposal
*/
public void setTriggerCharacters(char[] triggerCharacters) {
fTriggerCharacters= triggerCharacters;
}
/**
* 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
*/
public void setCursorPosition(int cursorPosition) {
Assert.isTrue(cursorPosition >= 0);
fCursorPosition= cursorPosition;
fContextInformationPosition= (fContextInformation != null ? fCursorPosition : -1);
}
/* protected void addInclude(IRequiredInclude[] inc, CFileElementWorkingCopy tu) {
AddIncludeOperation op= new AddIncludeOperation(fEditor, tu, inc, false);
try {
ProgressMonitorDialog dialog= new ProgressMonitorDialog(getShell());
dialog.run(false, true, op);
} catch (InvocationTargetException e) {
e.printStackTrace();
MessageDialog.openError(getShell(), CEditorMessages.getString("AddIncludeOnSelection.error.message1"), e.getTargetException().getMessage()); //$NON-NLS-1$
} catch (InterruptedException e) {
// Do nothing. Operation has been canceled.
}
} */
protected void applyIncludes(IDocument document) {
//AddIncludeOperation(ITextEditor ed, CFileElementWorkingCopy tu, IRequiredInclude[] includes, boolean save) {
//if (fIncludeDeclaration == null) {
return;
//}
/* ICompilationUnit cu= (ICompilationUnit) JavaModelUtil.findElementOfKind(fImportDeclaration, IJavaElement.COMPILATION_UNIT);
if (cu != null) {
try {
IType[] types= cu.getTypes();
if (types.length == 0 || types[0].getSourceRange().getOffset() > fReplacementOffset) {
// do not add import for code assist on import statements
return;
}
String[] prefOrder= ImportOrganizePreferencePage.getImportOrderPreference();
int threshold= ImportOrganizePreferencePage.getImportNumberThreshold();
ImportsStructure impStructure= new ImportsStructure(cu, prefOrder, threshold, true);
impStructure.addImport(fImportDeclaration.getElementName());
// will modify the document as the CU works on the document
impStructure.create(false, null);
} catch (CoreException e) {
JavaPlugin.log(e);
}
} */
}
/*
* @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;
fReplacementLength = offset - fReplacementOffset;
//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) == '(';
//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 {
String string;
if (trigger == (char) 0) {
string= fReplacementString;
replace(document, fReplacementOffset, fReplacementLength, replacementStringCopy);
} else {
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)) {
buffer.insert(fCursorPosition, trigger);
++fCursorPosition;
}
string= buffer.toString();
replace(document, fReplacementOffset, fReplacementLength, string);
}
if (fTextViewer != null && string != null) {
int index= string.indexOf("()"); //$NON-NLS-1$
if (index != -1 && index + 1 == fCursorPosition) {
//IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
//if (preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS)) {
int newOffset= fReplacementOffset + fCursorPosition;
LinkedPositionManager manager= new LinkedPositionManager(document);
manager.addPosition(newOffset, 0);
LinkedPositionUI editor= new LinkedPositionUI(fTextViewer, manager);
editor.setExitPolicy(new ExitPolicy(')'));
editor.setFinalCaretOffset(newOffset + 1);
editor.enter();
//}
}
}
/*
* 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;
} catch (BadLocationException x) {
// ignore
}
}
// #6410 - File unchanged but dirtied by code assist
private void replace(IDocument document, int offset, int length, String string) throws BadLocationException {
if (!document.get(offset, length).equals(string))
document.replace(offset, length, string);
}
/*
* @see ICompletionProposal#getSelection
*/
public Point getSelection(IDocument document) {
return new Point(fReplacementOffset + fCursorPosition, 0);
}
/*
* @see ICompletionProposal#getContextInformation()
*/
public IContextInformation getContextInformation() {
return fContextInformation;
}
/*
* @see ICompletionProposal#getImage()
*/
public Image getImage() {
return fImage;
}
/*
* @see ICompletionProposal#getDisplayString()
*/
public String getDisplayString() {
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() {
return fAdditionalInfoString;
}
/*
* @see ICompletionProposalExtension#getTriggerCharacters()
*/
public char[] getTriggerCharacters() {
return fTriggerCharacters;
}
/*
* @see ICompletionProposalExtension#getContextInformationPosition()
*/
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.
* @return Returns a int
*/
public int getReplacementOffset() {
return fReplacementOffset;
}
/**
* Sets the replacement offset.
* @param replacementOffset The replacement offset to set
*/
public void setReplacementOffset(int replacementOffset) {
Assert.isTrue(replacementOffset >= 0);
fReplacementOffset= replacementOffset;
}
/**
* Gets the replacement length.
* @return Returns a int
*/
public int getReplacementLength() {
return fReplacementLength;
}
/**
* Sets the replacement length.
* @param replacementLength The replacementLength to set
*/
public void setReplacementLength(int replacementLength) {
Assert.isTrue(replacementLength >= 0);
fReplacementLength= replacementLength;
}
/**
* Gets the replacement string.
* @return Returns a String
*/
public String getReplacementString() {
return fReplacementString;
}
/**
* Sets the replacement string.
* @param replacementString The replacement string to set
*/
public void setReplacementString(String replacementString) {
fReplacementString= replacementString;
}
/**
* Sets the image.
* @param image The image to set
*/
public void setImage(Image image) {
fImage= image;
}
/**
* Gets the proposal's relevance.
* @return Returns a int
*/
public int getRelevance() {
return fRelevance;
}
/**
* Sets the proposal's relevance.
* @param relevance The relevance to set
*/
public void setRelevance(int relevance) {
fRelevance= relevance;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return fDisplayString.hashCode()
+ fReplacementString.hashCode()
+ ((fAdditionalInfoString == null) ? 0 : fAdditionalInfoString.hashCode());
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object other) {
if(!(other instanceof CCompletionProposal))
return false;
if(!(fDisplayString.equals(((CCompletionProposal)other).fDisplayString)))
return false;
if(!(fReplacementString.equals(((CCompletionProposal)other).fReplacementString)))
return false;
if((fAdditionalInfoString != null) && (((CCompletionProposal)other).fAdditionalInfoString != null) && (!(fAdditionalInfoString.equals(((CCompletionProposal)other).fAdditionalInfoString))))
return false;
return true;
}
private static class ExitPolicy implements LinkedPositionUI.ExitPolicy {
final char fExitCharacter;
public ExitPolicy(char exitCharacter) {
fExitCharacter= exitCharacter;
}
/*
* @see org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, org.eclipse.swt.events.VerifyEvent, int, int)
*/
public ExitFlags doExit(LinkedPositionManager manager, VerifyEvent event, int offset, int length) {
if (event.character == fExitCharacter) {
if (manager.anyPositionIncludes(offset, length))
return new ExitFlags(LinkedPositionUI.COMMIT| LinkedPositionUI.UPDATE_CARET, false);
else
return new ExitFlags(LinkedPositionUI.COMMIT, true);
}
switch (event.character) {
case '\b':
if (manager.getFirstPosition().length == 0)
return new ExitFlags(0, true);
else
return null;
case '\n':
case '\r':
case ';':
return new ExitFlags(LinkedPositionUI.COMMIT, true);
default:
return null;
}
}
}
}