Merged content_assist_participants to HEAD
diff --git a/org.eclipse.jface.text/component.xml b/org.eclipse.jface.text/component.xml
index 712a1b1..890ea45 100644
--- a/org.eclipse.jface.text/component.xml
+++ b/org.eclipse.jface.text/component.xml
@@ -24,6 +24,7 @@
</package>
<package name="org.eclipse.jface.text.contentassist">
+ <type name="ContentAssistEvent" instantiate="false" subclass="false"/>
</package>
<package name="org.eclipse.jface.text.formatter">
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ContentAssistInvocationContext.java b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ContentAssistInvocationContext.java
new file mode 100644
index 0000000..7fab65d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/contentassist/ContentAssistInvocationContext.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.contentassist;
+
+/**
+ * Describes the context of an invocation of content assist. For a text editor, the context would
+ * typically include the document (or the viewer) and the selection range, while source code editors
+ * may provide specific context information such as an AST.
+ * <p>
+ * An invocation context may also compute additional context information on demand and cache it to
+ * make it available to all {@link org.eclipse.jface.text.contentassist.ICompletionProposalComputer}s
+ * contributing proposals to one content assist invocation.
+ * </p>
+ * <p>
+ * Clients may subclass but must be careful to adhere to the described
+ * {@link #equals(Object) equality} contract.
+ * </p>
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class ContentAssistInvocationContext {
+
+ /**
+ * Invocation contexts are equal if they describe the same context and are of the same type.
+ * This implementation checks for <code>null</code> values and class equality. Subclasses
+ * should extend this method by adding checks for their context relevant fields (but not
+ * necessarily cached values).
+ * <p>
+ * Example:
+ *
+ * <pre>
+ * class MyContext extends ContentAssistInvocationContext {
+ * private final Object fState;
+ * private Object fCachedInfo;
+ *
+ * ...
+ *
+ * public boolean equals(Object obb) {
+ * if (!super.equals(obj))
+ * return false;
+ * MyContext other= (MyContext) obj;
+ * return fState.equals(other.fState);
+ * }
+ * }
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * Subclasses should also extend {@link Object#hashCode()}.
+ * </p>
+ *
+ * @param obj {@inheritDoc}
+ * @return {@inheritDoc}
+ */
+ public boolean equals(Object obj) {
+ if (obj == null)
+ return false;
+ if (!getClass().equals(obj.getClass()))
+ return false;
+
+ return true;
+ }
+
+ /*
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return 23459213; // random
+ }
+
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java
index fc369e5..648fdcd 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java
@@ -227,7 +227,7 @@
setCustomInformationControlCreator(null);
// compute subject area
- setMargins(4, -2);
+ setMargins(4, 0);
Rectangle area= fProposalTable.getBounds();
area.x= 0; // subject area is the whole subject control
area.y= 0;
@@ -242,12 +242,12 @@
*/
protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl);
- Point size= subjectControl.getSize();
+ Point size= subjectControl.getShell().getSize();
- Rectangle otherTrim= subjectControl.getShell().computeTrim(0, 0, 0, 0);
- size.x += otherTrim.width;
- size.y += otherTrim.height;
-
+// Rectangle otherTrim= subjectControl.getShell().computeTrim(0, 0, 0, 0);
+// size.x += otherTrim.width;
+// size.y += otherTrim.height;
+//
if (informationControl instanceof IInformationControlExtension3) {
Rectangle thisTrim= ((IInformationControlExtension3)informationControl).computeTrim();
size.x -= thisTrim.width;
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
index 54f9862..09056ed 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
@@ -23,15 +23,21 @@
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
@@ -39,6 +45,7 @@
import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
@@ -201,6 +208,29 @@
* @since 3.1.1
*/
private boolean fIsFilterPending= false;
+ /**
+ * The info message at the bottom of the popup, or <code>null</code> for no popup (if
+ * ContentAssistant does not provide one).
+ *
+ * @since 3.2
+ */
+ private Label fMessageText;
+ /**
+ * The font used for <code>fMessageText</code> or null; dispose when done.
+ *
+ * @since 3.2
+ */
+ private Font fMessageTextFont;
+ /**
+ * The most recent completion offset (used to determine repeteated invocation)
+ *
+ * @since 3.2
+ */
+ private int fLastCompletionOffset;
+ /**
+ * Usually <code>true</code>, <code>false</code> if the popup is displaying an empty list after repeated invocation.
+ */
+ private boolean fHasProposals;
/**
@@ -248,7 +278,8 @@
final Control control= fContentAssistSubjectControlAdapter.getControl();
if (!Helper.okToUse(fProposalShell) && control != null && !control.isDisposed()) {
-
+ fContentAssistant.resetRepetition();
+
// add the listener before computing the proposals so we don't move the caret
// when the user types fast.
fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener);
@@ -258,36 +289,55 @@
fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
fFilterOffset= fInvocationOffset;
+ fLastCompletionOffset= fFilterOffset;
fComputedProposals= computeProposals(fInvocationOffset);
int count= (fComputedProposals == null ? 0 : fComputedProposals.length);
if (count == 0) {
-
- if (!autoActivated)
- control.getDisplay().beep();
-
- hide();
-
- } else {
-
- if (count == 1 && !autoActivated && canAutoInsert(fComputedProposals[0])) {
-
- insertProposal(fComputedProposals[0], (char) 0, 0, fInvocationOffset);
- hide();
-
- } else {
- createProposalSelector();
- setProposals(fComputedProposals, false);
- displayProposals();
+ if (fContentAssistant.recomputeOnRepetition()) {
+ fComputedProposals= computeProposals(fInvocationOffset);
+ count= (fComputedProposals == null ? 0 : fComputedProposals.length);
}
+ if (count == 0) {
+ if (!autoActivated)
+ control.getDisplay().beep();
+
+ hide();
+ return;
+ }
+ }
+
+ if (count == 1 && !autoActivated && canAutoInsert(fComputedProposals[0])) {
+ insertProposal(fComputedProposals[0], (char) 0, 0, fInvocationOffset);
+ hide();
+ } else {
+ createProposalSelector();
+ setProposals(fComputedProposals, false);
+ displayProposals();
}
}
});
+ } else {
+ if (fLastCompletionOffset == fFilterOffset) {
+ handleRepeatedInvocation();
+ } else {
+ fLastCompletionOffset= fFilterOffset;
+ }
+
}
return getErrorMessage();
}
+ private void handleRepeatedInvocation() {
+ ICompletionProposal[] recomputed= null;
+ if (fContentAssistant.recomputeOnRepetition()) {
+ recomputed= computeProposals(fFilterOffset);
+ if (recomputed != null)
+ setProposals(recomputed, false);
+ }
+ }
+
/**
* Returns the completion proposal available at the given offset of the
* viewer's document. Delegates the work to the content assistant.
@@ -334,16 +384,32 @@
fProposalTable.setLocation(0, 0);
if (fAdditionalInfoController != null)
- fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+ fAdditionalInfoController.setSizeConstraints(50, 10, true, true);
GridLayout layout= new GridLayout();
layout.marginWidth= 0;
layout.marginHeight= 0;
+ layout.verticalSpacing= 1;
fProposalShell.setLayout(layout);
+
+ String message= fContentAssistant.getMessage();
+ if (message != null) {
+ fMessageText= new Label(fProposalShell, SWT.RIGHT);
+ GridData textData= new GridData(SWT.FILL, SWT.BOTTOM, true, false);
+ fMessageText.setLayoutData(textData);
+ fMessageText.setText(message + " "); //$NON-NLS-1$
+ Font font= fMessageText.getFont();
+ Display display= fProposalShell.getDisplay();
+ FontData[] fontDatas= font.getFontData();
+ for (int i= 0; i < fontDatas.length; i++)
+ fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+ fMessageTextFont= new Font(display, fontDatas);
+ fMessageText.setFont(fMessageTextFont);
+ fMessageText.setCursor(fProposalShell.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+ }
GridData data= new GridData(GridData.FILL_BOTH);
-
Point size= fContentAssistant.restoreCompletionProposalPopupSize();
if (size != null) {
fProposalTable.setLayoutData(data);
@@ -353,6 +419,7 @@
data.widthHint= 300;
fProposalTable.setLayoutData(data);
fProposalShell.pack();
+ fSize= fProposalShell.getSize();
}
fProposalShell.addControlListener(new ControlListener() {
@@ -368,14 +435,16 @@
fSize= fProposalShell.getSize();
}
});
-
+
if (!"carbon".equals(SWT.getPlatform())) //$NON-NLS-1$
- fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+ fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_GRAY));
Color c= fContentAssistant.getProposalSelectorBackground();
if (c == null)
c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
fProposalTable.setBackground(c);
+ if (fMessageText != null)
+ fMessageText.setBackground(c);
c= fContentAssistant.getProposalSelectorForeground();
if (c == null)
@@ -401,6 +470,18 @@
fProposalTable.setHeaderVisible(false);
fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
+
+ if (fMessageText != null) {
+ fMessageText.addMouseListener(new MouseAdapter() {
+ public void mouseUp(MouseEvent e) {
+ fLastCompletionOffset= fFilterOffset;
+ handleRepeatedInvocation();
+ }
+
+ public void mouseDown(MouseEvent e) {
+ }
+ });
+ }
}
/*
@@ -469,6 +550,9 @@
*/
private void insertProposal(ICompletionProposal p, char trigger, int stateMask, final int offset) {
+ if (!fHasProposals)
+ return;
+
fInserting= true;
IRewriteTarget target= null;
IEditingSupport helper= new IEditingSupport() {
@@ -580,6 +664,13 @@
fProposalShell.dispose();
fProposalShell= null;
}
+
+ if (fMessageTextFont != null) {
+ fMessageTextFont.dispose();
+ fMessageTextFont= null;
+ }
+
+ fLastCompletionOffset= -1;
}
/**
@@ -637,6 +728,15 @@
ICompletionProposal oldProposal= getSelectedProposal();
if (oldProposal instanceof ICompletionProposalExtension2 && fViewer != null)
((ICompletionProposalExtension2) oldProposal).unselected(fViewer);
+
+ if (proposals == null || proposals.length == 0) {
+ proposals= new ICompletionProposal[] { new CompletionProposal(JFaceTextMessages.getString("CompletionProposalPopup.no_proposals"), fFilterOffset, 0, 0)}; //$NON-NLS-1$
+ fProposalTable.setEnabled(false);
+ fHasProposals= false;
+ } else {
+ fProposalTable.setEnabled(true);
+ fHasProposals= true;
+ }
fFilteredProposals= proposals;
final int newLen= proposals.length;
@@ -1032,7 +1132,12 @@
*/
public String incrementalComplete() {
if (Helper.okToUse(fProposalShell) && fFilteredProposals != null) {
- completeCommonPrefix();
+ if (fLastCompletionOffset == fFilterOffset) {
+ handleRepeatedInvocation();
+ } else {
+ fLastCompletionOffset= fFilterOffset;
+ completeCommonPrefix();
+ }
} else {
final Control control= fContentAssistSubjectControlAdapter.getControl();
@@ -1047,13 +1152,24 @@
fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
fFilterOffset= fInvocationOffset;
+ fLastCompletionOffset= fFilterOffset;
fFilteredProposals= computeProposals(fInvocationOffset);
int count= (fFilteredProposals == null ? 0 : fFilteredProposals.length);
if (count == 0) {
- control.getDisplay().beep();
- hide();
- } else if (count == 1 && canAutoInsert(fFilteredProposals[0])) {
+ if (fContentAssistant.recomputeOnRepetition()) {
+ fFilteredProposals= computeProposals(fInvocationOffset);
+ count= (fFilteredProposals == null ? 0 : fFilteredProposals.length);
+ }
+
+ if (count == 0) {
+ control.getDisplay().beep();
+ hide();
+ return;
+ }
+ }
+
+ if (count == 1 && canAutoInsert(fFilteredProposals[0])) {
insertProposal(fFilteredProposals[0], (char) 0, 0, fInvocationOffset);
hide();
} else {
@@ -1202,6 +1318,7 @@
fContentAssistSubjectControlAdapter.setSelectedRange(fFilterOffset + postfix.length(), 0);
fContentAssistSubjectControlAdapter.revealRange(fFilterOffset + postfix.length(), 0);
+ fLastCompletionOffset= fFilterOffset + postfix.length();
return false;
} catch (BadLocationException e) {
@@ -1284,4 +1401,10 @@
return insertion;
}
+
+ public void setMessage(String message) {
+ Assert.isNotNull(message);
+ if (isActive() && fMessageText != null)
+ fMessageText.setText(message + " "); //$NON-NLS-1$
+ }
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java
new file mode 100644
index 0000000..8293542
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistEvent.java
@@ -0,0 +1,44 @@
+package org.eclipse.jface.text.contentassist;
+
+/**
+ * Describes the state that the content assistant is in when completing proposals.
+ * <p>
+ * Clients may use this class.
+ * </p>
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class ContentAssistEvent {
+ ContentAssistEvent(ContentAssistant ca, TextContentAssistInvocationContext ctx,
+ IContentAssistProcessor proc, int rep) {
+ assistant= ca;
+ context= ctx;
+ processor= proc;
+ repetition= rep;
+ }
+
+ /**
+ * The content assistant computing proposals.
+ */
+ public final ContentAssistant assistant;
+
+ /**
+ * The content assist invocation context.
+ */
+ public final TextContentAssistInvocationContext context;
+
+ /**
+ * The content assist processor that will be queried for proposals, depending on the current
+ * partition type.
+ */
+ public final IContentAssistProcessor processor;
+
+ /**
+ * The repetition count, i.e. how many times that content assist was invoked in one content
+ * assist session. The counter is reset once the popup is closed.
+ */
+ public final int repetition;
+}
\ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
index d52fb04..f0f1267 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
@@ -10,8 +10,10 @@
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -42,10 +44,6 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
-import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
-import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
-import org.eclipse.jface.dialogs.IDialogSettings;
-
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
@@ -60,13 +58,17 @@
import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
+import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+
/**
* The standard implementation of the <code>IContentAssistant</code> interface.
* Usually, clients instantiate this class and configure it before using it.
*/
public class ContentAssistant implements IContentAssistant, IContentAssistantExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
-
+
/**
* A generic closer class used to monitor various
* interface events in order to determine whether
@@ -751,6 +753,26 @@
* @since 3.0
*/
private boolean fIsPrefixCompletionEnabled= false;
+ /**
+ * The list of completion listeners.
+ *
+ * @since 3.2
+ */
+ private List fCompletionListeners= new ArrayList();
+ /**
+ * The repetition count - counts how many times code completion was invoked at the same offset
+ * in a single code completion session.
+ *
+ * @since 3.2
+ */
+ private int fRepetition;
+ /**
+ * The message to display at the bottom of the proposal popup, or <code>null</code> if no
+ * message should be shown.
+ *
+ * @since 3.2
+ */
+ private String fMessage= null;
/**
* Creates a new content assistant. The content assistant is not automatically activated,
@@ -1398,6 +1420,7 @@
*/
protected void possibleCompletionsClosed() {
storeCompletionProposalPopupSize();
+ fRepetition= 0;
}
/*
@@ -1531,6 +1554,7 @@
IContentAssistProcessor p= getProcessor(viewer, offset);
if (p != null) {
+ fireEvent(viewer, offset, p);
result= p.computeCompletionProposals(viewer, offset);
fLastErrorMessage= p.getErrorMessage();
}
@@ -1878,4 +1902,69 @@
public boolean hasProposalPopupFocus() {
return fProposalPopup.hasFocus();
}
+
+ /**
+ * Sets the caption message displayed at the bottom of the completion proposal popup.
+ *
+ * @param message the message
+ * @since 3.2
+ */
+ public void setMessage(String message) {
+ Assert.isNotNull(message);
+ fMessage= message;
+ if (fProposalPopup != null)
+ fProposalPopup.setMessage(message);
+ }
+
+ String getMessage() {
+ return fMessage;
+ }
+
+ /**
+ * Adds a completion listener that will be informed before proposals are computed.
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @param listener the listener
+ * @since 3.2
+ */
+ public void addCompletionListener(ICompletionListener listener) {
+ Assert.isNotNull(listener);
+ fCompletionListeners.add(listener);
+ }
+
+ /**
+ * Removes the completion listener.
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @param listener the listener to remove
+ * @since 3.2
+ */
+ public void removeListener(ICompletionListener listener) {
+ fCompletionListeners.remove(listener);
+ }
+
+ private void fireEvent(ITextViewer viewer, int offset, IContentAssistProcessor processor) {
+ ContentAssistEvent event= new ContentAssistEvent(this, new TextContentAssistInvocationContext(viewer, offset), processor, fRepetition);
+ for (Iterator it= new ArrayList(fCompletionListeners).iterator(); it.hasNext();) {
+ ICompletionListener listener= (ICompletionListener) it.next();
+ listener.computingProposals(event);
+ }
+ }
+
+ boolean recomputeOnRepetition() {
+ fRepetition++;
+ return doRepetitionHandling();
+ }
+
+ private boolean doRepetitionHandling() {
+ return fMessage != null;
+ }
+
+ void resetRepetition() {
+ fRepetition= 0;
+ }
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListener.java
new file mode 100644
index 0000000..49f8432
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionListener.java
@@ -0,0 +1,21 @@
+package org.eclipse.jface.text.contentassist;
+
+/**
+ * A completion listener is informed before the content assisant computes completion proposals.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ICompletionListener {
+ /**
+ * Informs the receiver that completion proposals will be computed.
+ *
+ * @param event the content assist event
+ */
+ void computingProposals(ContentAssistEvent event);
+}
\ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalComputer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalComputer.java
new file mode 100644
index 0000000..dc67a3b
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalComputer.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.contentassist;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Computes completions and context information displayed by a content assistant.
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ICompletionProposalComputer {
+
+ /**
+ * Returns a list of completion proposals valid at the given invocation context.
+ *
+ * @param context the context of the content assist invocation
+ * @param monitor a progress monitor to report progress. The monitor is private to this
+ * invocation, i.e. there is no need for the receiver to spawn a sub monitor.
+ * @return an array of completion proposals (element type: {@link ICompletionProposal})
+ */
+ List computeCompletionProposals(TextContentAssistInvocationContext context, IProgressMonitor monitor);
+
+ /**
+ * Returns context information objects valid at the given invocation context.
+ *
+ * @param context the context of the content assist invocation
+ * @param monitor a progress monitor to report progress. The monitor is private to this
+ * invocation, i.e. there is no need for the receiver to spawn a sub monitor.
+ * @return an array of context information objects (element type: {@link IContextInformation})
+ */
+ List computeContextInformation(TextContentAssistInvocationContext context, IProgressMonitor monitor);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
index fc0436b..b35cad9 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.properties
@@ -13,3 +13,4 @@
InfoPopup.info_delay_timer_name=AdditionalInfo Delay
ContentAssistant.assist_delay_timer_name=AutoAssist Delay
+CompletionProposalPopup.no_proposals=no proposals
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/TextContentAssistInvocationContext.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/TextContentAssistInvocationContext.java
new file mode 100644
index 0000000..3c5669c
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/TextContentAssistInvocationContext.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.contentassist;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+
+import org.eclipse.jface.contentassist.ContentAssistInvocationContext;
+
+/**
+ * A content assist invocation context for text viewers. The context knows the
+ * viewer, the invocation offset and can lazily compute the identifier
+ * prefix preceding the invocation offset.
+ * <p>
+ * Clients may instantiate and subclass.
+ * </p>
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public class TextContentAssistInvocationContext extends ContentAssistInvocationContext {
+
+ /* state */
+ private final ITextViewer fViewer;
+ private final int fOffset;
+
+ /* cached additional info */
+ private CharSequence fPrefix;
+
+ /**
+ * Equivalent to
+ * {@linkplain #TextContentAssistInvocationContext(ITextViewer, int) TextContentAssistInvocationContext(viewer, viewer.getSelectedRange().x)}.
+ *
+ * @param viewer the text viewer that content assist is invoked in
+ */
+ public TextContentAssistInvocationContext(ITextViewer viewer) {
+ this(viewer, viewer.getSelectedRange().x);
+ }
+
+ /**
+ * Creates a new context for the given viewer and offset.
+ *
+ * @param viewer the text viewer that content assist is invoked in
+ * @param offset the offset into the viewer's document where content assist is invoked at
+ */
+ public TextContentAssistInvocationContext(ITextViewer viewer, int offset) {
+ Assert.isNotNull(viewer);
+ fViewer= viewer;
+ fOffset= offset;
+ }
+
+ /**
+ * Returns the invocation offset.
+ *
+ * @return the invocation offset
+ */
+ public final int getInvocationOffset() {
+ return fOffset;
+ }
+
+ /**
+ * Returns the viewer.
+ *
+ * @return the viewer
+ */
+ public final ITextViewer getViewer() {
+ return fViewer;
+ }
+
+ /**
+ * Shortcut for <code>getViewer().getDocument()</code>.
+ *
+ * @return the viewer's document
+ */
+ public IDocument getDocument() {
+ return getViewer().getDocument();
+ }
+
+ /**
+ * Computes the identifier (as specified by {@link Character#isJavaIdentifierPart(char)}) that
+ * immediately precedes the invocation offset.
+ *
+ * @return the prefix preceding the content assist invocation offset
+ * @throws BadLocationException if accessing the document fails
+ */
+ public CharSequence computeIdentifierPrefix() throws BadLocationException {
+ if (fPrefix == null) {
+ IDocument document= getDocument();
+ int end= getInvocationOffset();
+ int start= end;
+ while (--start >= 0) {
+ if (!Character.isJavaIdentifierPart(document.getChar(start)))
+ break;
+ }
+ start++;
+ fPrefix= document.get(start, end - start);
+ }
+
+ return fPrefix;
+ }
+
+ /*
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (!super.equals(obj))
+ return false;
+ TextContentAssistInvocationContext other= (TextContentAssistInvocationContext) obj;
+ return fViewer.equals(other.fViewer) && fOffset == other.fOffset;
+ }
+
+ /*
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode() << 5 | fViewer.hashCode() << 3 | fOffset;
+ }
+}
diff --git a/org.eclipse.ui.workbench.texteditor/component.xml b/org.eclipse.ui.workbench.texteditor/component.xml
index dace458..c465592 100644
--- a/org.eclipse.ui.workbench.texteditor/component.xml
+++ b/org.eclipse.ui.workbench.texteditor/component.xml
@@ -20,6 +20,7 @@
<type name="ITextEditorDropTargetListener" implement="false"/>
<type name="RevertToSavedAction" subclass="false"/>
<type name="SaveAction" subclass="false"/>
+ <type name="HippieProposalComputer" subclass="false"/>
</package>
<package name="org.eclipse.ui.texteditor.link">
diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/HippieProposalComputer.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/HippieProposalComputer.java
new file mode 100644
index 0000000..6e2054b
--- /dev/null
+++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/HippieProposalComputer.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.texteditor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalComputer;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext;
+
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.texteditor.HippieCompletionEngine;
+
+/**
+ * A completion proposal computer for hippie word completions.
+ * <p>
+ * Clients may instantiate.
+ * </p>
+ * <p>
+ * XXX this API is provisional and may change anytime during the course of 3.2
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class HippieProposalComputer implements ICompletionProposalComputer {
+
+ private static final class Proposal implements ICompletionProposal, ICompletionProposalExtension, ICompletionProposalExtension2, ICompletionProposalExtension3, ICompletionProposalExtension4 {
+
+ private final String fString;
+ private final String fPrefix;
+ private final int fOffset;
+
+ public Proposal(String string, String prefix, int offset) {
+ fString= string;
+ fPrefix= prefix;
+ fOffset= offset;
+ }
+
+ public void apply(IDocument document) {
+ apply(null, '\0', 0, fOffset);
+ }
+
+ public Point getSelection(IDocument document) {
+ return new Point(fOffset + fString.length(), 0);
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public String getDisplayString() {
+ return fPrefix + fString;
+ }
+
+ public Image getImage() {
+ return null;
+ }
+
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ public void apply(IDocument document, char trigger, int offset) {
+ try {
+ String replacement= fString.substring(offset - fOffset);
+ document.replace(offset, 0, replacement);
+ } catch (BadLocationException x) {
+ // TODO Auto-generated catch block
+ x.printStackTrace();
+ }
+ }
+
+ public boolean isValidFor(IDocument document, int offset) {
+ return validate(document, offset, null);
+ }
+
+ public char[] getTriggerCharacters() {
+ return null;
+ }
+
+ public int getContextInformationPosition() {
+ return 0;
+ }
+
+ public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
+ apply(viewer.getDocument(), trigger, offset);
+ }
+
+ public void selected(ITextViewer viewer, boolean smartToggle) {
+ }
+
+ public void unselected(ITextViewer viewer) {
+ }
+
+ public boolean validate(IDocument document, int offset, DocumentEvent event) {
+ try {
+ int prefixStart= fOffset - fPrefix.length();
+ return offset >= fOffset && offset < fOffset + fString.length() && document.get(prefixStart, offset - (prefixStart)).equals((fPrefix + fString).substring(0, offset - prefixStart));
+ } catch (BadLocationException x) {
+ return false;
+ }
+ }
+
+ public IInformationControlCreator getInformationControlCreator() {
+ return null;
+ }
+
+ public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
+ return fPrefix + fString;
+ }
+
+ public int getPrefixCompletionStart(IDocument document, int completionOffset) {
+ return fOffset - fPrefix.length();
+ }
+
+ public boolean isAutoInsertable() {
+ return true;
+ }
+
+ }
+
+ private final HippieCompletionEngine fEngine= new HippieCompletionEngine();
+
+ /**
+ * Creates a new hippie completion proposal computer.
+ */
+ public HippieProposalComputer() {
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public List computeCompletionProposals(TextContentAssistInvocationContext context, IProgressMonitor monitor) {
+ try {
+ String prefix= context.computeIdentifierPrefix().toString();
+ if (prefix.length() == 0)
+ return Collections.EMPTY_LIST;
+
+ String[] suggestions= getSuggestions(context.getViewer(), context.getInvocationOffset(), prefix);
+
+ List result= new ArrayList();
+ for (int i= 0; i < suggestions.length; i++) {
+ String string= suggestions[i];
+ if (string.length() > 0)
+ result.add(createProposal(string, prefix, context.getInvocationOffset()));
+ }
+
+ return result;
+
+ } catch (BadLocationException x) {
+ // TODO Auto-generated catch block
+ x.printStackTrace();
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ private ICompletionProposal createProposal(String string, String prefix, int offset) {
+ return new Proposal(string, prefix, offset);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public List computeContextInformation(TextContentAssistInvocationContext context, IProgressMonitor monitor) {
+ // no context informations for hippie completions
+ return Collections.EMPTY_LIST;
+ }
+
+
+ /**
+ * Return the list of suggestions from the current document. First the
+ * document is searched backwards from the caret position and then forwards.
+ *
+ * @param offset
+ * @param viewer
+ * @param prefix the completion prefix
+ * @return all possible completions that were found in the current document
+ * @throws BadLocationException if accessing the document fails
+ */
+ private ArrayList createSuggestionsFromOpenDocument(ITextViewer viewer, int offset, String prefix) throws BadLocationException {
+ IDocument document= viewer.getDocument();
+ ArrayList completions= new ArrayList();
+ completions.addAll(fEngine.getCompletionsBackwards(document, prefix, offset));
+ completions.addAll(fEngine.getCompletionsForward(document, prefix, offset));
+
+ return completions;
+ }
+
+ /**
+ * Create the array of suggestions. It scans all open text editors and
+ * prefers suggestions from the currently open editor. It also adds the
+ * empty suggestion at the end.
+ *
+ * @param viewer
+ * @param offset
+ * @param prefix the prefix to search for
+ * @return the list of all possible suggestions in the currently open
+ * editors
+ * @throws BadLocationException if accessing the current document fails
+ */
+ private String[] getSuggestions(ITextViewer viewer, int offset, String prefix) throws BadLocationException {
+
+ ArrayList suggestions= createSuggestionsFromOpenDocument(viewer, offset, prefix);
+ IDocument currentDocument= viewer.getDocument();
+
+ IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IEditorReference editorReferences[]= window.getActivePage().getEditorReferences();
+
+ for (int i= 0; i < editorReferences.length; i++) {
+ IEditorPart editor= editorReferences[i].getEditor(false); // don't create!
+ if (editor instanceof ITextEditor) {
+ ITextEditor textEditor= (ITextEditor) editor;
+ IEditorInput input= textEditor.getEditorInput();
+ IDocument doc= textEditor.getDocumentProvider().getDocument(input);
+ if (!currentDocument.equals(doc))
+ suggestions.addAll(fEngine.getCompletions(doc, prefix));
+ }
+ }
+ // add the empty suggestion
+ suggestions.add(""); //$NON-NLS-1$
+
+ List uniqueSuggestions= fEngine.makeUnique(suggestions);
+
+ return (String[]) uniqueSuggestions.toArray(new String[0]);
+ }
+}