All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.text.contentassist.ContentAssistant Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2016 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
 *     Guy Gurfinkel, [email protected] - [content assist][api] provide better access to ContentAssistant - https://bugs.eclipse.org/bugs/show_bug.cgi?id=169954
 *     Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
 *     Marcel Bruch, [email protected] - [content assist] Allow to re-sort proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=350991
 *     John Glassmyer, [email protected] - catch Content Assist exceptions to protect navigation keys - http://bugs.eclipse.org/434901
 *******************************************************************************/
package org.eclipse.jface.text.contentassist;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;

import org.eclipse.core.commands.IHandler;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;

import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.util.OpenStrategy;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IEventConsumer;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.IWidgetTokenKeeper;
import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
import org.eclipse.jface.text.IWidgetTokenOwner;
import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
import org.eclipse.jface.text.TextUtilities;


/**
 * The standard implementation of the IContentAssistant interface. Usually, clients
 * instantiate this class and configure it before using it.
 */
public class ContentAssistant implements IContentAssistant, IContentAssistantExtension, IContentAssistantExtension2, IContentAssistantExtension3, IContentAssistantExtension4, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {



	/**
	 * Content assist command identifier for 'select next proposal'.
	 *
	 * @since 3.4
	 */
	public static final String SELECT_NEXT_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectNextProposal"; //$NON-NLS-1$
	/**
	 * Content assist command identifier for 'select previous proposal'.
	 *
	 * @since 3.4
	 */
	public static final String SELECT_PREVIOUS_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectPreviousProposal"; //$NON-NLS-1$


	/**
	 * A generic closer class used to monitor various interface events in order to determine whether
	 * content-assist should be terminated and all associated windows closed.
	 */
	class Closer implements ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {

		/** The shell that a ControlListener is registered with. */
		private Shell fShell;
		/**
		 * The control that a MouseListener, aFocusListener and a
		 * DisposeListener are registered with.
		 */
		private Control fControl;

		/**
		 * Installs this closer on it's viewer's text widget.
		 */
		protected void install() {
			Control control= fContentAssistSubjectControlAdapter.getControl();
			fControl= control;
			if (Helper.okToUse(control)) {

				Shell shell= control.getShell();
				fShell= shell;
				shell.addControlListener(this);

				control.addMouseListener(this);
				control.addFocusListener(this);

				/*
				 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
				 * Internal Errors
				 */
				control.addDisposeListener(this);
			}
			if (fViewer != null)
				fViewer.addViewportListener(this);
		}

		/**
		 * Uninstalls this closer from the viewer's text widget.
		 */
		protected void uninstall() {
			Control shell= fShell;
			fShell= null;
			if (Helper.okToUse(shell))
				shell.removeControlListener(this);

			Control control= fControl;
			fControl= null;
			if (Helper.okToUse(control)) {

				control.removeMouseListener(this);
				control.removeFocusListener(this);

				/*
				 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
				 * Internal Errors
				 */
				control.removeDisposeListener(this);
			}

			if (fViewer != null)
				fViewer.removeViewportListener(this);
		}

		@Override
		public void controlResized(ControlEvent e) {
			hide();
		}

		@Override
		public void controlMoved(ControlEvent e) {
			hide();
		}

		@Override
		public void mouseDown(MouseEvent e) {
			hide();
		}

		@Override
		public void mouseUp(MouseEvent e) {
		}

		@Override
		public void mouseDoubleClick(MouseEvent e) {
			hide();
		}

		@Override
		public void focusGained(FocusEvent e) {
		}

		@Override
		public void focusLost(FocusEvent e) {
			Control control= fControl;
			if (Helper.okToUse(control)) {
				Display d= control.getDisplay();
				if (d != null) {
					d.asyncExec(new Runnable() {
						@Override
						public void run() {
							if (!fProposalPopup.hasFocus() && (fContextInfoPopup == null || !fContextInfoPopup.hasFocus()))
								hide();
						}
					});
				}
			}
		}

		/*
		 * @seeDisposeListener#widgetDisposed(DisposeEvent)
		 */
		@Override
		public void widgetDisposed(DisposeEvent e) {
			/*
			 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal
			 * Errors
			 */
			hide();
		}

		@Override
		public void viewportChanged(int topIndex) {
			hide();
		}
	}

	/**
	 * An implementation of IContentAssistListener, this class is used to monitor
	 * key events in support of automatic activation of the content assistant. If enabled, the
	 * implementation utilizes a thread to watch for input characters matching the activation
	 * characters specified by the content assist processor, and if detected, will wait the
	 * indicated delay interval before activating the content assistant.
	 *
	 * @since 3.4 protected, was added in 2.1 as private class
	 */
	protected class AutoAssistListener extends KeyAdapter implements Runnable, VerifyKeyListener {

		private Thread fThread;
		private boolean fIsReset= false;
		private Object fMutex= new Object();
		private int fShowStyle;

		private final static int SHOW_PROPOSALS= 1;
		private final static int SHOW_CONTEXT_INFO= 2;

		protected AutoAssistListener() {
		}

		protected void start(int showStyle) {
			fShowStyle= showStyle;
			fThread= new Thread(this, JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
			fThread.start();
		}

		@Override
		public void run() {
			try {
				while (true) {
					synchronized (fMutex) {
						if (fAutoActivationDelay != 0)
							fMutex.wait(fAutoActivationDelay);
						if (fIsReset) {
							fIsReset= false;
							continue;
						}
					}
					showAssist(fShowStyle);
					break;
				}
			} catch (InterruptedException e) {
			}
			fThread= null;
		}

		protected void reset(int showStyle) {
			synchronized (fMutex) {
				fShowStyle= showStyle;
				fIsReset= true;
				fMutex.notifyAll();
			}
		}

		protected void stop() {
			Thread threadToStop= fThread;
			if (threadToStop != null && threadToStop.isAlive())
				threadToStop.interrupt();
		}

		private boolean contains(char[] characters, char character) {
			if (characters != null) {
				for (int i= 0; i < characters.length; i++) {
					if (character == characters[i])
						return true;
				}
			}
			return false;
		}

		@Override
		public void keyPressed(KeyEvent e) {
			// Only act on typed characters and ignore modifier-only events
			if (e.character == 0 && (e.keyCode & SWT.KEYCODE_BIT) == 0)
				return;

			if (e.character != 0 && (e.stateMask == SWT.ALT))
				return;

			// Only act on characters that are trigger candidates. This
			// avoids computing the model selection on every keystroke
			if (computeAllAutoActivationTriggers().indexOf(e.character) < 0) {
				stop();
				return;
			}

			int showStyle;
			int pos= fContentAssistSubjectControlAdapter.getSelectedRange().x;
			char[] activation;

			activation= fContentAssistSubjectControlAdapter.getCompletionProposalAutoActivationCharacters(ContentAssistant.this, pos);

			if (contains(activation, e.character) && !isProposalPopupActive())
				showStyle= SHOW_PROPOSALS;
			else {
				activation= fContentAssistSubjectControlAdapter.getContextInformationAutoActivationCharacters(ContentAssistant.this, pos);
				if (contains(activation, e.character) && !isContextInfoPopupActive())
					showStyle= SHOW_CONTEXT_INFO;
				else {
					stop();
					return;
				}
			}

			if (fThread != null && fThread.isAlive())
				reset(showStyle);
			else
				start(showStyle);
		}

		@Override
		public void verifyKey(VerifyEvent event) {
			keyPressed(event);
		}

		protected void showAssist(final int showStyle) {
			final Control control= fContentAssistSubjectControlAdapter.getControl();
			if (control == null)
				return;

			final Display d= control.getDisplay();
			if (d == null)
				return;

			try {
				d.syncExec(new Runnable() {
					@Override
					public void run() {
						if (isProposalPopupActive())
							return;

						if (control.isDisposed() || !control.isFocusControl())
							return;

						if (showStyle == SHOW_PROPOSALS) {
							if (!prepareToShowCompletions(true))
								return;
							fProposalPopup.showProposals(true);
							fLastAutoActivation= System.currentTimeMillis();
						} else if (showStyle == SHOW_CONTEXT_INFO && fContextInfoPopup != null) {
							promoteKeyListener();
							fContextInfoPopup.showContextProposals(true);
						}
					}
				});
			} catch (SWTError e) {
			}
		}
	}

	/**
	 * The layout manager layouts the various windows associated with the content assistant based on
	 * the settings of the content assistant.
	 */
	class LayoutManager implements Listener {

		// Presentation types.
		/** The presentation type for the proposal selection popup. */
		public final static int LAYOUT_PROPOSAL_SELECTOR= 0;
		/** The presentation type for the context selection popup. */
		public final static int LAYOUT_CONTEXT_SELECTOR= 1;
		/** The presentation type for the context information hover . */
		public final static int LAYOUT_CONTEXT_INFO_POPUP= 2;

		int fContextType= LAYOUT_CONTEXT_SELECTOR;
		Shell[] fShells= new Shell[3];
		Object[] fPopups= new Object[3];

		protected void add(Object popup, Shell shell, int type, int offset) {
			Assert.isNotNull(popup);
			Assert.isTrue(shell != null && !shell.isDisposed());
			checkType(type);

			if (fShells[type] != shell) {
				if (fShells[type] != null)
					fShells[type].removeListener(SWT.Dispose, this);
				shell.addListener(SWT.Dispose, this);
				fShells[type]= shell;
			}

			fPopups[type]= popup;
			if (type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP)
				fContextType= type;

			layout(type, offset);
			adjustListeners(type);
		}

		protected void checkType(int type) {
			Assert.isTrue(type == LAYOUT_PROPOSAL_SELECTOR ||
				type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP);
		}

		@Override
		public void handleEvent(Event event) {
			Widget source= event.widget;
			source.removeListener(SWT.Dispose, this);

			int type= getShellType(source);
			checkType(type);
			fShells[type]= null;

			switch (type) {
				case LAYOUT_PROPOSAL_SELECTOR:
					if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
							Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
						// Restore event notification to the tip popup.
						addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
					}
					break;

				case LAYOUT_CONTEXT_SELECTOR:
					if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
						if (fProposalPopupOrientation == PROPOSAL_STACKED)
							layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
						// Restore event notification to the proposal popup.
						addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
					}
					fContextType= LAYOUT_CONTEXT_INFO_POPUP;
					break;

				case LAYOUT_CONTEXT_INFO_POPUP:
					if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
						if (fContextInfoPopupOrientation == CONTEXT_INFO_BELOW)
							layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
					}
					fContextType= LAYOUT_CONTEXT_SELECTOR;
					break;
			}
		}

		protected int getShellType(Widget shell) {
			for (int i= 0; i < fShells.length; i++) {
				if (fShells[i] == shell)
					return i;
			}
			return -1;
		}

		/**
		 * Layouts the popup defined by type at the given widget offset.
		 *
		 * @param type the kind of popup to layout
		 * @param offset the widget offset
		 */
		protected void layout(int type, int offset) {
			switch (type) {
				case LAYOUT_PROPOSAL_SELECTOR:
					layoutProposalSelector(offset);
					break;
				case LAYOUT_CONTEXT_SELECTOR:
					layoutContextSelector(offset);
					break;
				case LAYOUT_CONTEXT_INFO_POPUP:
					layoutContextInfoPopup(offset);
					break;
			}
		}

		protected void layoutProposalSelector(int offset) {
			if (fContextType == LAYOUT_CONTEXT_INFO_POPUP &&
					fContextInfoPopupOrientation == CONTEXT_INFO_BELOW &&
					Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
				// Stack proposal selector beneath the tip box.
				Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
				Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
				shell.setLocation(getStackedLocation(shell, parent));
			} else if (fContextType != LAYOUT_CONTEXT_SELECTOR ||
						!Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
				// There are no other presentations to be concerned with,
				// so place the proposal selector beneath the cursor line.
				Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
				CompletionProposalPopup popup= (CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR];
				shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
			} else {
				CompletionProposalPopup popup= ((CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR]);
				switch (fProposalPopupOrientation) {
					case PROPOSAL_REMOVE: {
						// Remove the tip selector and place the
						// proposal selector beneath the cursor line.
						fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
						Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
						shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
						break;
					}
					case PROPOSAL_OVERLAY: {
						// Overlay the tip selector with the proposal selector.
						Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
						shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
						break;
					}
					case PROPOSAL_STACKED: {
						// Stack the proposal selector beneath the tip selector.
						Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
						Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
						shell.setLocation(getStackedLocation(shell, parent));
						break;
					}
				}
			}
		}

		protected void layoutContextSelector(int offset) {
			// Always place the context selector beneath the cursor line.
			Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
			shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, null));

			if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
				switch (fProposalPopupOrientation) {
					case PROPOSAL_REMOVE:
						// Remove the proposal selector.
						fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
						break;

					case PROPOSAL_OVERLAY:
						// The proposal selector has been overlaid by the tip selector.
						break;

					case PROPOSAL_STACKED: {
						// Stack the proposal selector beneath the tip selector.
						shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
						Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
						shell.setLocation(getStackedLocation(shell, parent));
						break;
					}
				}
			}
		}

		protected void layoutContextInfoPopup(int offset) {
			switch (fContextInfoPopupOrientation) {
				case CONTEXT_INFO_ABOVE: {
					// Place the popup above the cursor line.
					Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
					shell.setBounds(computeBoundsAboveBelow(shell, shell.getSize(), offset));
					break;
				}
				case CONTEXT_INFO_BELOW: {
					// Place the popup beneath the cursor line.
					Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
					parent.setBounds(computeBoundsBelowAbove(parent, parent.getSize(), offset, null));
					if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
						// Stack the proposal selector beneath the context info popup.
						Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
						shell.setLocation(getStackedLocation(shell, parent));
					}
					break;
				}
			}
		}

		/**
		 * Moves point such that rectangle does not bleed outside of
		 * bounds. All coordinates must have the same reference.
		 *
		 * @param point the point to move if needed
		 * @param shellSize the size of the shell that may be moved
		 * @param bounds the bounds
		 * @since 3.3
		 */
		protected void constrainLocation(Point point, Point shellSize, Rectangle bounds) {
			if (point.x + shellSize.x > bounds.x + bounds.width)
				point.x= bounds.x + bounds.width - shellSize.x;

			if (point.x < bounds.x)
				point.x= bounds.x;

			if (point.y + shellSize.y > bounds.y + bounds.height)
				point.y= bounds.y + bounds.height - shellSize.y;

			if (point.y < bounds.y)
				point.y= bounds.y;
		}

		protected Rectangle constrainHorizontally(Rectangle rect, Rectangle bounds) {
			// clip width
			if (rect.width > bounds.width)
				rect.width= bounds.width;

			if (rect.x + rect.width > bounds.x + bounds.width)
				rect.x= bounds.x + bounds.width - rect.width;
			if (rect.x < bounds.x)
				rect.x= bounds.x;

			return rect;
		}

		/**
		 * Returns the display bounds for shell such that it appears right above
		 * offset, or below it if above is not suitable. The returned bounds lie
		 * within the monitor at the caret location and never overlap with the caret line.
		 *
		 * @param shell the shell to compute the placement for
		 * @param preferred the preferred size for shell
		 * @param offset the caret offset in the subject control
		 * @return the point right above offset in display coordinates
		 * @since 3.3
		 */
		protected Rectangle computeBoundsAboveBelow(Shell shell, Point preferred, int offset) {
			Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
			Display display= subjectControl.getDisplay();
			Rectangle caret= getCaretRectangle(offset);
			Monitor monitor= getClosestMonitor(display, caret);
			Rectangle bounds= monitor.getClientArea();
			Geometry.moveInside(caret, bounds);

			int spaceAbove= caret.y - bounds.y;
			int caretLowerY= caret.y + caret.height;
			int spaceBelow= bounds.y + bounds.height - caretLowerY;
			Rectangle rect;
			if (spaceAbove >= preferred.y)
				rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
			else if (spaceBelow >= preferred.y)
				rect= new Rectangle(caret.x, caretLowerY, preferred.x, preferred.y);
			// we can't fit in the preferred size - squeeze into larger area
			else if (spaceBelow <= spaceAbove)
				rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
			else
				rect= new Rectangle(caret.x, caretLowerY, preferred.x, spaceBelow);

			return constrainHorizontally(rect, bounds);
		}

		/**
		 * Returns the display bounds for shell such that it appears right below
		 * offset, or above it if below is not suitable. The returned bounds lie
		 * within the monitor at the caret location and never overlap with the caret line.
		 *
		 * @param shell the shell to compute the placement for
		 * @param preferred the preferred size for shell
		 * @param offset the caret offset in the subject control
		 * @param popup a popup to inform if the location was switched to above, null to do nothing
		 * @return the point right below offset in display coordinates
		 * @since 3.3
		 */
		protected Rectangle computeBoundsBelowAbove(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
			Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
			Display display= subjectControl.getDisplay();
			Rectangle caret= getCaretRectangle(offset);
			Monitor monitor= getClosestMonitor(display, caret);
			Rectangle bounds= monitor.getClientArea();
			Geometry.moveInside(caret, bounds);

			int threshold= popup == null ? Integer.MAX_VALUE : popup.getMinimalHeight();
			int spaceAbove= caret.y - bounds.y;
			int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
			Rectangle rect;
			boolean switched= false;
			if (spaceBelow >= preferred.y)
				rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, preferred.y);
			// squeeze in below if we have at least threshold space
			else if (spaceBelow >= threshold)
				rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
			else if (spaceAbove >= preferred.y) {
				rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
				switched= true;
			} else if (spaceBelow >= spaceAbove) {
				// we can't fit in the preferred size - squeeze into larger area
				rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
			} else {
				rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
				switched= true;
			}

			if (popup != null)
				popup.switchedPositionToAbove(switched);

			return constrainHorizontally(rect, bounds);
		}

		private Rectangle getCaretRectangle(int offset) {
			Point location= fContentAssistSubjectControlAdapter.getLocationAtOffset(offset);
			Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
			Point controlSize= subjectControl.getSize();
			constrainLocation(location, new Point(0, 0), new Rectangle(0, 0, controlSize.x, controlSize.y));
			location= subjectControl.toDisplay(location);
			Rectangle subjectRectangle= new Rectangle(location.x, location.y, 1, fContentAssistSubjectControlAdapter.getLineHeight());
			return subjectRectangle;
		}

		protected Point getStackedLocation(Shell shell, Shell parent) {
			Point p= parent.getLocation();
			Point size= parent.getSize();
			p.x += size.x / 4;
			p.y += size.y;

			p= parent.toDisplay(p);

			Point shellSize= shell.getSize();
			Monitor monitor= getClosestMonitor(parent.getDisplay(), new Rectangle(p.x, p.y, 0, 0));
			Rectangle displayBounds= monitor.getClientArea();
			constrainLocation(p, shellSize, displayBounds);

			return p;
		}

		protected void adjustListeners(int type) {
			switch (type) {
				case LAYOUT_PROPOSAL_SELECTOR:
					if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
							Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
						// Disable event notification to the tip selector.
						removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
					break;
				case LAYOUT_CONTEXT_SELECTOR:
					if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
						// Disable event notification to the proposal selector.
						removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
					break;
				case LAYOUT_CONTEXT_INFO_POPUP:
					break;
			}
		}

		/**
		 * Copied from org.eclipse.jface.window.Window. Returns the monitor whose client area
		 * contains the given point. If no monitor contains the point, returns the monitor that is
		 * closest to the point. If this is ever made public, it should be moved into a separate
		 * utility class.
		 *
		 * @param toSearch point to find (display coordinates)
		 * @param rectangle rectangle to find (display coordinates)
		 * @return the monitor closest to the given point
		 * @since 3.3
		 */
		private Monitor getClosestMonitor(Display toSearch, Rectangle rectangle) {
			int closest = Integer.MAX_VALUE;

			Point toFind= Geometry.centerPoint(rectangle);
			Monitor[] monitors = toSearch.getMonitors();
			Monitor result = monitors[0];

			for (int idx = 0; idx < monitors.length; idx++) {
				Monitor current = monitors[idx];

				Rectangle clientArea = current.getClientArea();

				if (clientArea.contains(toFind)) {
					return current;
				}

				int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
				if (distance < closest) {
					closest = distance;
					result = current;
				}
			}

			return result;
		}
	}

	/**
	 * Internal key listener and event consumer.
	 */
	class InternalListener implements VerifyKeyListener, IEventConsumer {

		/**
		 * Verifies key events by notifying the registered listeners. Each listener is allowed to
		 * indicate that the event has been handled and should not be further processed.
		 *
		 * @param e the verify event
		 * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
		 */
		@Override
		public void verifyKey(VerifyEvent e) {
			IContentAssistListener[] listeners= fListeners.clone();
			for (int i= 0; i < listeners.length; i++) {
				if (listeners[i] != null) {
					if (!listeners[i].verifyKey(e) || !e.doit)
						break;
				}
			}
			if (fAutoAssistListener != null)
				fAutoAssistListener.keyPressed(e);
		}

		/*
		 * @see IEventConsumer#processEvent
		 */
		@Override
		public void processEvent(VerifyEvent event) {

			installKeyListener();

			IContentAssistListener[] listeners= fListeners.clone();
			for (int i= 0; i < listeners.length; i++) {
				if (listeners[i] != null) {
					listeners[i].processEvent(event);
					if (!event.doit)
						return;
				}
			}
		}
	}

	/**
	 * A subclass of ISafeRunnable which, in case of exception, logs a specified error message to the jface.text log and
	 * sets fLastErrorMessage to this message.
	 */
	private abstract class ExceptionLoggingSafeRunnable implements ISafeRunnable {
		private static final String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$

		private final String fMessageKey;

		/**
		 * @param messageKey key passed to JFaceTextMessages to lookup the text of the error message
		 */
		ExceptionLoggingSafeRunnable(String messageKey) {
			fMessageKey= messageKey;
		}

		@Override
		public void handleException(Throwable exception) {
			String message= JFaceTextMessages.getString(fMessageKey);

			IStatus status= new Status(IStatus.ERROR, PLUGIN_ID, message, exception);
			Platform.getLog(Platform.getBundle(PLUGIN_ID)).log(status);

			fLastErrorMessage= message;
		}
	}

	/**
	 * Dialog store constant for the x-size of the completion proposal pop-up
	 * 
	 * @since 3.0
	 */
	public static final String STORE_SIZE_X= "size.x"; //$NON-NLS-1$

	/**
	 * Dialog store constant for the y-size of the completion proposal pop-up
	 * 
	 * @since 3.0
	 */
	public static final String STORE_SIZE_Y= "size.y"; //$NON-NLS-1$
	
	/**
	 * Dialog store constant for the x-size of the context selector pop-up
	 * 
	 * @since 3.9
	 */
	public static final String STORE_CONTEXT_SELECTOR_POPUP_SIZE_X= "contextSelector.size.x"; //$NON-NLS-1$
	
	/**
	 * Dialog store constant for the y-size of the context selector pop-up
	 * 
	 * @since 3.9
	 */
	public static final String STORE_CONTEXT_SELECTOR_POPUP_SIZE_Y= "contextSelector.size.y"; //$NON-NLS-1$


	// Content-Assist Listener types
	final static int CONTEXT_SELECTOR= 0;
	final static int PROPOSAL_SELECTOR= 1;
	final static int CONTEXT_INFO_POPUP= 2;

	/**
	 * The popup priority: > linked position proposals and hover pop-ups. Default value:
	 * 20;
	 *
	 * @since 3.0
	 */
	public static final int WIDGET_PRIORITY= 20;
	private static final int DEFAULT_AUTO_ACTIVATION_DELAY= 500;

	private static final String COMPLETION_ERROR_MESSAGE_KEY= "ContentAssistant.error_computing_completion"; //$NON-NLS-1$
	private static final String CONTEXT_ERROR_MESSAGE_KEY= "ContentAssistant.error_computing_context"; //$NON-NLS-1$

	private BoldStylerProvider fBoldStylerProvider;

	private IInformationControlCreator fInformationControlCreator;
	private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
	private boolean fIsAutoActivated= false;
	private boolean fIsAutoInserting= false;
	private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
	private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
	private Map fProcessors;

	/**
	 * The partitioning.
	 *
	 * @since 3.0
	 */
	private String fPartitioning;

	private Color fContextInfoPopupBackground;
	private Color fContextInfoPopupForeground;
	private Color fContextSelectorBackground;
	private Color fContextSelectorForeground;
	private Color fProposalSelectorBackground;
	private Color fProposalSelectorForeground;

	private ITextViewer fViewer;
	private String fLastErrorMessage;

	private Closer fCloser;
	LayoutManager fLayoutManager;
	private AutoAssistListener fAutoAssistListener;
	private InternalListener fInternalListener;
	private CompletionProposalPopup fProposalPopup;
	private ContextInformationPopup fContextInfoPopup;

	/**
	 * Flag which tells whether a verify key listener is hooked.
	 *
	 * @since 3.0
	 */
	private boolean fVerifyKeyListenerHooked= false;
	private IContentAssistListener[] fListeners= new IContentAssistListener[4];
	/**
	 * The content assist subject control.
	 *
	 * @since 3.0
	 */
	private IContentAssistSubjectControl fContentAssistSubjectControl;
	/**
	 * The content assist subject control's shell.
	 *
	 * @since 3.2
	 */
	private Shell fContentAssistSubjectControlShell;
	/**
	 * The content assist subject control's shell traverse listener.
	 *
	 * @since 3.2
	 */
	private TraverseListener fCASCSTraverseListener;
	/**
	 * The content assist subject control adapter.
	 *
	 * @since 3.0
	 */
	private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
	/**
	 * The dialog settings for the control's bounds.
	 *
	 * @since 3.0
	 */
	private IDialogSettings fDialogSettings;
	/**
	 * Prefix completion setting.
	 *
	 * @since 3.0
	 */
	private boolean fIsPrefixCompletionEnabled= false;
	/**
	 * The list of completion listeners.
	 *
	 * @since 3.2
	 */
	private ListenerList fCompletionListeners= new ListenerList<>(ListenerList.IDENTITY);
	/**
	 * The message to display at the bottom of the proposal popup.
	 *
	 * @since 3.2
	 */
	private String fMessage= ""; //$NON-NLS-1$
	/**
	 * The cycling mode property.
	 *
	 * @since 3.2
	 */
	private boolean fIsRepetitionMode= false;
	/**
	 * The show empty property.
	 *
	 * @since 3.2
	 */
	private boolean fShowEmptyList= false;
	/**
	 * The message line property.
	 *
	 * @since 3.2
	 */
	private boolean fIsStatusLineVisible;
	/**
	 * The last system time when auto activation performed.
	 *
	 * @since 3.2
	 */
	private long fLastAutoActivation= Long.MIN_VALUE;
	/**
	 * The iteration key sequence to listen for, or null.
	 *
	 * @since 3.2
	 */
	private KeySequence fRepeatedInvocationKeySequence;

	/**
	 * Maps handler to command identifiers.
	 *
	 * @since 3.4
	 */
	private Map fHandlers;

	/**
	 * Tells whether colored labels support is enabled.
	 *
	 * @since 3.4
	 */
	private boolean fIsColoredLabelsSupportEnabled= false;

	/**
	 * The sorter to be used for sorting the proposals or null if no sorting is
	 * requested.
	 * 
	 * @since 3.8
	 */
	private ICompletionProposalSorter fSorter;

	/**
	 * Creates a new content assistant. The content assistant is not automatically activated,
	 * overlays the completion proposals with context information list if necessary, and shows the
	 * context information above the location at which it was activated. If auto activation will be
	 * enabled, without further configuration steps, this content assistant is activated after a 500
	 * milliseconds delay. It uses the default partitioning.
	 */
	public ContentAssistant() {
		fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
	}

	/**
	 * Sets the document partitioning this content assistant is using.
	 *
	 * @param partitioning the document partitioning for this content assistant
	 * @since 3.0
	 */
	public void setDocumentPartitioning(String partitioning) {
		Assert.isNotNull(partitioning);
		fPartitioning= partitioning;
	}

	@Override
	public String getDocumentPartitioning() {
		return fPartitioning;
	}

	/**
	 * Registers a given content assist processor for a particular content type. If there is already
	 * a processor registered for this type, the new processor is registered instead of the old one.
	 *
	 * @param processor the content assist processor to register, or null to remove
	 *        an existing one
	 * @param contentType the content type under which to register
	 */
	public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {

		Assert.isNotNull(contentType);

		if (fProcessors == null)
			fProcessors= new HashMap<>();

		if (processor == null)
			fProcessors.remove(contentType);
		else
			fProcessors.put(contentType, processor);
	}

	/*
	 * @see IContentAssistant#getContentAssistProcessor
	 */
	@Override
	public IContentAssistProcessor getContentAssistProcessor(String contentType) {
		if (fProcessors == null)
			return null;

		return fProcessors.get(contentType);
	}

	/**
	 * Computes the sorted set of all auto activation trigger characters.
	 *
	 * @return the sorted set of all auto activation trigger characters
	 * @since 3.1
	 */
	private String computeAllAutoActivationTriggers() {
		if (fProcessors == null)
			return ""; //$NON-NLS-1$

		StringBuffer buf= new StringBuffer(5);
		Iterator> iter= fProcessors.entrySet().iterator();
		while (iter.hasNext()) {
			Entry entry= iter.next();
			IContentAssistProcessor processor= entry.getValue();
			char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
			if (triggers != null)
				buf.append(triggers);
			triggers= processor.getContextInformationAutoActivationCharacters();
			if (triggers != null)
				buf.append(triggers);
		}
		return buf.toString();
	}

	/**
	 * Enables the content assistant's auto activation mode.
	 *
	 * @param enabled indicates whether auto activation is enabled or not
	 */
	public void enableAutoActivation(boolean enabled) {
		fIsAutoActivated= enabled;
		manageAutoActivation(fIsAutoActivated);
	}

	/**
	 * Enables the content assistant's auto insertion mode. If enabled, the content assistant
	 * inserts a proposal automatically if it is the only proposal. In the case of ambiguities, the
	 * user must make the choice.
	 *
	 * @param enabled indicates whether auto insertion is enabled or not
	 * @since 2.0
	 */
	public void enableAutoInsert(boolean enabled) {
		fIsAutoInserting= enabled;
	}

	/**
	 * Returns whether this content assistant is in the auto insertion mode or not.
	 *
	 * @return true if in auto insertion mode
	 * @since 2.0
	 */
	boolean isAutoInserting() {
		return fIsAutoInserting;
	}

	/**
	 * Installs and uninstall the listeners needed for auto activation.
	 *
	 * @param start true if listeners must be installed, false if they
	 *        must be removed
	 * @since 2.0
	 */
	private void manageAutoActivation(boolean start) {
		if (start) {

			if ((fContentAssistSubjectControlAdapter != null) && fAutoAssistListener == null) {
				fAutoAssistListener= createAutoAssistListener();
				// For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
				if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
					fContentAssistSubjectControlAdapter.appendVerifyKeyListener(fAutoAssistListener);
				else
					fContentAssistSubjectControlAdapter.addKeyListener(fAutoAssistListener);
			}

		} else if (fAutoAssistListener != null) {
			// For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
			if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
				fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fAutoAssistListener);
			else
				fContentAssistSubjectControlAdapter.removeKeyListener(fAutoAssistListener);
			fAutoAssistListener= null;
		}
	}

	/**
	 * This method allows subclasses to provide their own {@link AutoAssistListener}.
	 *
	 * @return a new auto assist listener
	 * @since 3.4
	 */
	protected AutoAssistListener createAutoAssistListener() {
		return new AutoAssistListener();
	}

	/**
	 * Sets the delay after which the content assistant is automatically invoked if the cursor is
	 * behind an auto activation character.
	 * 
	 * @param delay the auto activation delay (as of 3.6 a negative argument will be set to 0)
	 */
	public void setAutoActivationDelay(int delay) {
		fAutoActivationDelay= Math.max(0, delay);
	}

	/**
	 * Gets the delay after which the content assistant is automatically invoked if the cursor is
	 * behind an auto activation character.
	 * 
	 * @return the auto activation delay (will not be negative)
	 * @since 3.4
	 */
	public int getAutoActivationDelay() {
		return fAutoActivationDelay;
	}

	/**
	 * Sets the proposal pop-ups' orientation. The following values may be used:
	 * 
    *
  • PROPOSAL_OVERLAY

    * proposal popup windows should overlay each other *

  • *
  • PROPOSAL_REMOVE

    * any currently shown proposal popup should be closed *

  • *
  • PROPOSAL_STACKED

    * proposal popup windows should be vertical stacked, with no overlap, * beneath the line containing the current cursor location *

  • *
* * @param orientation the popup's orientation */ public void setProposalPopupOrientation(int orientation) { fProposalPopupOrientation= orientation; } /** * Sets the context information popup's orientation. * The following values may be used: *
    *
  • CONTEXT_ABOVE

    * context information popup should always appear above the line containing * the current cursor location *

  • *
  • CONTEXT_BELOW

    * context information popup should always appear below the line containing * the current cursor location *

  • *
* * @param orientation the popup's orientation */ public void setContextInformationPopupOrientation(int orientation) { fContextInfoPopupOrientation= orientation; } /** * Sets the context information popup's background color. * * @param background the background color */ public void setContextInformationPopupBackground(Color background) { fContextInfoPopupBackground= background; } /** * Returns the background of the context information popup. * * @return the background of the context information popup * @since 2.0 */ Color getContextInformationPopupBackground() { return fContextInfoPopupBackground; } /** * Sets the context information popup's foreground color. * * @param foreground the foreground color * @since 2.0 */ public void setContextInformationPopupForeground(Color foreground) { fContextInfoPopupForeground= foreground; } /** * Returns the foreground of the context information popup. * * * @return the foreground of the context information popup * @since 2.0 */ Color getContextInformationPopupForeground() { return fContextInfoPopupForeground; } /** * Sets the proposal selector's background color. *

* Note: As of 3.4, you should only call this * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_BACKGROUND_COLOR}. *

* * @param background the background color * @since 2.0 */ public void setProposalSelectorBackground(Color background) { fProposalSelectorBackground= background; } /** * Returns the custom background color of the proposal selector. * * @return the background of the proposal selector or null if not set * @since 2.0 */ Color getProposalSelectorBackground() { return fProposalSelectorBackground; } /** * Sets the proposal's foreground color. *

* Note: As of 3.4, you should only call this * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_FOREGROUND_COLOR}. *

* * @param foreground the foreground color * @since 2.0 */ public void setProposalSelectorForeground(Color foreground) { fProposalSelectorForeground= foreground; } /** * Returns the custom foreground color of the proposal selector. * * @return the foreground of the proposal selector or null if not set * @since 2.0 */ Color getProposalSelectorForeground() { return fProposalSelectorForeground; } /** * Sets the {@link BoldStylerProvider} used to emphasize matches in a proposal's styled display * string. * * @param boldStylerProvider the bold styler provider * * @see ICompletionProposalExtension7#getStyledDisplayString(IDocument, int, BoldStylerProvider) * @since 3.11 */ void setBoldStylerProvider(BoldStylerProvider boldStylerProvider) { fBoldStylerProvider= boldStylerProvider; } /** * Returns the {@link BoldStylerProvider} used to emphasize matches in a proposal's styled * display string. * * @see ICompletionProposalExtension7#getStyledDisplayString(IDocument, int, BoldStylerProvider) * * @return the {@link BoldStylerProvider}, or null if not set * @since 3.11 */ BoldStylerProvider getBoldStylerProvider() { return fBoldStylerProvider; } /** * Sets the context selector's background color. * * @param background the background color * @since 2.0 */ public void setContextSelectorBackground(Color background) { fContextSelectorBackground= background; } /** * Returns the background of the context selector. * * @return the background of the context selector * @since 2.0 */ Color getContextSelectorBackground() { return fContextSelectorBackground; } /** * Sets the context selector's foreground color. * * @param foreground the foreground color * @since 2.0 */ public void setContextSelectorForeground(Color foreground) { fContextSelectorForeground= foreground; } /** * Returns the foreground of the context selector. * * @return the foreground of the context selector * @since 2.0 */ Color getContextSelectorForeground() { return fContextSelectorForeground; } /** * Sets the information control creator for the additional information control. * * @param creator the information control creator for the additional information control * @since 2.0 */ public void setInformationControlCreator(IInformationControlCreator creator) { fInformationControlCreator= creator; } /* * @see IControlContentAssistant#install(IContentAssistSubjectControl) * @since 3.0 */ protected void install(IContentAssistSubjectControl contentAssistSubjectControl) { fContentAssistSubjectControl= contentAssistSubjectControl; fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl); install(); } /* * @see IContentAssist#install * @since 3.0 */ @Override public void install(ITextViewer textViewer) { fViewer= textViewer; fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer); install(); } protected void install() { fLayoutManager= new LayoutManager(); fInternalListener= new InternalListener(); AdditionalInfoController controller= null; if (fInformationControlCreator != null) controller= new AdditionalInfoController(fInformationControlCreator, OpenStrategy.getPostSelectionDelay()); fContextInfoPopup= fContentAssistSubjectControlAdapter.createContextInfoPopup(this); fProposalPopup= fContentAssistSubjectControlAdapter.createCompletionProposalPopup(this, controller); fProposalPopup.setSorter(fSorter); registerHandler(SELECT_NEXT_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_NEXT)); registerHandler(SELECT_PREVIOUS_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_PREVIOUS)); if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) { fContentAssistSubjectControlShell= fContentAssistSubjectControlAdapter.getControl().getShell(); fCASCSTraverseListener= new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_ESCAPE && isProposalPopupActive()) e.doit= false; } }; fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener); } manageAutoActivation(fIsAutoActivated); } /* * @see IContentAssist#uninstall */ @Override public void uninstall() { hide(); if (fBoldStylerProvider != null) { fBoldStylerProvider.dispose(); fBoldStylerProvider= null; } manageAutoActivation(false); if (fHandlers != null) { fHandlers.clear(); fHandlers= null; } if (fCloser != null) { fCloser.uninstall(); fCloser= null; } if (Helper.okToUse(fContentAssistSubjectControlShell)) fContentAssistSubjectControlShell.removeTraverseListener(fCASCSTraverseListener); fCASCSTraverseListener= null; fContentAssistSubjectControlShell= null; fViewer= null; fContentAssistSubjectControl= null; fContentAssistSubjectControlAdapter= null; } /** * Adds the given shell of the specified type to the layout. Valid types are defined by * LayoutManager. * * @param popup a content assist popup * @param shell the shell of the content-assist popup * @param type the type of popup * @param visibleOffset the offset at which to layout the popup relative to the offset of the * viewer's visible region * @since 2.0 */ void addToLayout(Object popup, Shell shell, int type, int visibleOffset) { fLayoutManager.add(popup, shell, type, visibleOffset); } /** * Layouts the registered popup of the given type relative to the given offset. The offset is * relative to the offset of the viewer's visible region. Valid types are defined by * LayoutManager. * * @param type the type of popup to layout * @param visibleOffset the offset at which to layout relative to the offset of the viewer's * visible region * @since 2.0 */ void layout(int type, int visibleOffset) { fLayoutManager.layout(type, visibleOffset); } /** * Returns the layout manager. * * @return the layout manager * @since 3.3 */ LayoutManager getLayoutManager() { return fLayoutManager; } /** * Notifies the controller that a popup has lost focus. * * @param e the focus event */ void popupFocusLost(FocusEvent e) { fCloser.focusLost(e); } /** * Returns the offset of the selection relative to the offset of the visible region. * * @return the offset of the selection relative to the offset of the visible region * @since 2.0 */ int getSelectionOffset() { return fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x; } /** * Returns whether the widget token could be acquired. The following are valid listener types: *
    *
  • AUTO_ASSIST
  • *
  • CONTEXT_SELECTOR
  • *
  • PROPOSAL_SELECTOR
  • *
  • CONTEXT_INFO_POPUP
  • *
* * @param type the listener type for which to acquire * @return true if the widget token could be acquired * @since 2.0 */ private boolean acquireWidgetToken(int type) { switch (type) { case CONTEXT_SELECTOR: case PROPOSAL_SELECTOR: if (fContentAssistSubjectControl instanceof IWidgetTokenOwnerExtension) { IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fContentAssistSubjectControl; return extension.requestWidgetToken(this, WIDGET_PRIORITY); } else if (fContentAssistSubjectControl instanceof IWidgetTokenOwner) { IWidgetTokenOwner owner= (IWidgetTokenOwner) fContentAssistSubjectControl; return owner.requestWidgetToken(this); } else if (fViewer instanceof IWidgetTokenOwnerExtension) { IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fViewer; return extension.requestWidgetToken(this, WIDGET_PRIORITY); } else if (fViewer instanceof IWidgetTokenOwner) { IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer; return owner.requestWidgetToken(this); } } return true; } /** * Registers a content assist listener. The following are valid listener types: *
    *
  • AUTO_ASSIST
  • *
  • CONTEXT_SELECTOR
  • *
  • PROPOSAL_SELECTOR
  • *
  • CONTEXT_INFO_POPUP
  • *
* Returns whether the listener could be added successfully. A listener can not be added if the * widget token could not be acquired. * * @param listener the listener to register * @param type the type of listener * @return true if the listener could be added */ boolean addContentAssistListener(IContentAssistListener listener, int type) { if (acquireWidgetToken(type)) { fListeners[type]= listener; if (fCloser == null && getNumberOfListeners() == 1) { fCloser= new Closer(); fCloser.install(); fContentAssistSubjectControlAdapter.setEventConsumer(fInternalListener); installKeyListener(); } else promoteKeyListener(); return true; } return false; } /** * Re-promotes the key listener to the first position, using prependVerifyKeyListener. This * ensures no other instance is filtering away the keystrokes underneath, if we've been up for a * while (e.g. when the context info is showing. * * @since 3.0 */ private void promoteKeyListener() { uninstallVerifyKeyListener(); installKeyListener(); } /** * Installs a key listener on the text viewer's widget. */ private void installKeyListener() { if (!fVerifyKeyListenerHooked) { if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) { fVerifyKeyListenerHooked= fContentAssistSubjectControlAdapter.prependVerifyKeyListener(fInternalListener); } } } /** * Releases the previously acquired widget token if the token is no longer necessary. The * following are valid listener types: *
    *
  • AUTO_ASSIST
  • *
  • CONTEXT_SELECTOR
  • *
  • PROPOSAL_SELECTOR
  • *
  • CONTEXT_INFO_POPUP
  • *
* * @param type the listener type * @since 2.0 */ private void releaseWidgetToken(int type) { if (fListeners[CONTEXT_SELECTOR] == null && fListeners[PROPOSAL_SELECTOR] == null) { IWidgetTokenOwner owner= null; if (fContentAssistSubjectControl instanceof IWidgetTokenOwner) owner= (IWidgetTokenOwner) fContentAssistSubjectControl; else if (fViewer instanceof IWidgetTokenOwner) owner= (IWidgetTokenOwner) fViewer; if (owner != null) owner.releaseWidgetToken(this); } } /** * Unregisters a content assist listener. * * @param listener the listener to unregister * @param type the type of listener * @see #addContentAssistListener(IContentAssistListener, int) */ void removeContentAssistListener(IContentAssistListener listener, int type) { fListeners[type]= null; if (getNumberOfListeners() == 0) { if (fCloser != null) { fCloser.uninstall(); fCloser= null; } uninstallVerifyKeyListener(); fContentAssistSubjectControlAdapter.setEventConsumer(null); } releaseWidgetToken(type); } /** * Uninstall the key listener from the text viewer's widget. * * @since 3.0 */ private void uninstallVerifyKeyListener() { if (fVerifyKeyListenerHooked) { if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fInternalListener); fVerifyKeyListenerHooked= false; } } /** * Returns the number of listeners. * * @return the number of listeners * @since 2.0 */ private int getNumberOfListeners() { int count= 0; for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) { if (fListeners[i] != null) ++count; } return count; } /* * @see IContentAssist#showPossibleCompletions */ @Override public String showPossibleCompletions() { if (!prepareToShowCompletions(false)) return null; if (fIsPrefixCompletionEnabled) return fProposalPopup.incrementalComplete(); return fProposalPopup.showProposals(false); } @Override public String completePrefix() { if (!prepareToShowCompletions(false)) return null; return fProposalPopup.incrementalComplete(); } /** * Prepares to show content assist proposals. It returns false if auto activation has kicked in * recently. * * @param isAutoActivated whether completion was triggered by auto activation * @return true if the caller should continue and show the proposals, * false otherwise. * @since 3.2 */ private boolean prepareToShowCompletions(boolean isAutoActivated) { if (!isAutoActivated) { int gracePeriod= Math.max(fAutoActivationDelay, 200); if (System.currentTimeMillis() < fLastAutoActivation + gracePeriod) { return false; } } promoteKeyListener(); fireSessionBeginEvent(isAutoActivated); return true; } /** * Callback to signal this content assistant that the presentation of the possible completions * has been stopped. * * @since 2.1 */ protected void possibleCompletionsClosed() { fLastAutoActivation= Long.MIN_VALUE; storeCompletionProposalPopupSize(); } /* * @see IContentAssist#showContextInformation */ @Override public String showContextInformation() { promoteKeyListener(); if (fContextInfoPopup != null) return fContextInfoPopup.showContextProposals(false); return null; } /** * Callback to signal this content assistant that the presentation of the context information * has been stopped. * * @since 2.1 */ protected void contextInformationClosed() { } /** * Requests that the specified context information to be shown. * * @param contextInformation the context information to be shown * @param offset the offset to which the context information refers to * @since 2.0 */ void showContextInformation(IContextInformation contextInformation, int offset) { if (fContextInfoPopup != null) fContextInfoPopup.showContextInformation(contextInformation, offset); } /** * Returns the current content assist error message. * * @return an error message or null if no error has occurred */ String getErrorMessage() { return fLastErrorMessage; } /** * Returns the content assist processor for the content type of the specified document position. * * @param viewer the text viewer * @param offset a offset within the document * @return a content-assist processor or null if none exists * @since 3.0 */ private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) { try { IDocument document= viewer.getDocument(); String type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true); return getContentAssistProcessor(type); } catch (BadLocationException x) { } return null; } /** * Returns the content assist processor for the content type of the specified document position. * * @param contentAssistSubjectControl the content assist subject control * @param offset a offset within the document * @return a content-assist processor or null if none exists * @since 3.0 */ private IContentAssistProcessor getProcessor(IContentAssistSubjectControl contentAssistSubjectControl, int offset) { try { IDocument document= contentAssistSubjectControl.getDocument(); String type; if (document != null) type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true); else type= IDocument.DEFAULT_CONTENT_TYPE; return getContentAssistProcessor(type); } catch (BadLocationException x) { } return null; } /** * Returns an array of completion proposals computed based on the specified document position. * The position is used to determine the appropriate content assist processor to invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return an array of completion proposals * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int) * @since 3.0 */ ICompletionProposal[] computeCompletionProposals( final IContentAssistSubjectControl contentAssistSubjectControl, final int offset) { fLastErrorMessage= null; final ICompletionProposal[][] result= { null }; final IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset); if (p instanceof ISubjectControlContentAssistProcessor) { // Ensure that the assist session ends cleanly even if the processor throws an exception. SafeRunner.run(new ExceptionLoggingSafeRunnable(COMPLETION_ERROR_MESSAGE_KEY) { @Override public void run() throws Exception { result[0]= ((ISubjectControlContentAssistProcessor) p) .computeCompletionProposals(contentAssistSubjectControl, offset); fLastErrorMessage= p.getErrorMessage(); } }); } return result[0]; } /** * Returns an array of completion proposals computed based on the specified document position. * The position is used to determine the appropriate content assist processor to invoke. * * @param viewer the viewer for which to compute the proposals * @param offset a document offset * @return an array of completion proposals or null if no proposals are possible * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int) */ ICompletionProposal[] computeCompletionProposals(final ITextViewer viewer, final int offset) { fLastErrorMessage= null; final ICompletionProposal[][] result= { null }; final IContentAssistProcessor p= getProcessor(viewer, offset); if (p != null) { // Ensure that the assist session ends cleanly even if the processor throws an exception. SafeRunner.run(new ExceptionLoggingSafeRunnable(COMPLETION_ERROR_MESSAGE_KEY) { @Override public void run() throws Exception { result[0]= p.computeCompletionProposals(viewer, offset); fLastErrorMessage= p.getErrorMessage(); } }); } return result[0]; } /** * Returns an array of context information objects computed based on the specified document * position. The position is used to determine the appropriate content assist processor to * invoke. * * @param viewer the viewer for which to compute the context information * @param offset a document offset * @return an array of context information objects * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int) */ IContextInformation[] computeContextInformation(final ITextViewer viewer, final int offset) { fLastErrorMessage= null; final IContextInformation[][] result= { null }; final IContentAssistProcessor p= getProcessor(viewer, offset); if (p != null) { // Ensure that the assist session ends cleanly even if the processor throws an exception. SafeRunner.run(new ExceptionLoggingSafeRunnable(CONTEXT_ERROR_MESSAGE_KEY) { @Override public void run() throws Exception { result[0]= p.computeContextInformation(viewer, offset); fLastErrorMessage= p.getErrorMessage(); } }); } return result[0]; } /** * Returns an array of context information objects computed based on the specified document * position. The position is used to determine the appropriate content assist processor to * invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return an array of context information objects * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int) * @since 3.0 */ IContextInformation[] computeContextInformation( final IContentAssistSubjectControl contentAssistSubjectControl, final int offset) { fLastErrorMessage= null; final IContextInformation[][] result= { null }; final IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset); if (p instanceof ISubjectControlContentAssistProcessor) { // Ensure that the assist session ends cleanly even if the processor throws an exception. SafeRunner.run(new ExceptionLoggingSafeRunnable(CONTEXT_ERROR_MESSAGE_KEY) { @Override public void run() throws Exception { result[0]= ((ISubjectControlContentAssistProcessor) p) .computeContextInformation(contentAssistSubjectControl, offset); fLastErrorMessage= p.getErrorMessage(); } }); } return result[0]; } /** * Returns the context information validator that should be used to determine when the currently * displayed context information should be dismissed. The position is used to determine the * appropriate content assist processor to invoke. * * @param viewer the text viewer * @param offset a document offset * @return an validator * @see IContentAssistProcessor#getContextInformationValidator() * @since 3.0 */ IContextInformationValidator getContextInformationValidator(ITextViewer viewer, int offset) { IContentAssistProcessor p= getProcessor(viewer, offset); return p != null ? p.getContextInformationValidator() : null; } /** * Returns the context information validator that should be used to determine when the currently * displayed context information should be dismissed. The position is used to determine the * appropriate content assist processor to invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return an validator * @see IContentAssistProcessor#getContextInformationValidator() * @since 3.0 */ IContextInformationValidator getContextInformationValidator(IContentAssistSubjectControl contentAssistSubjectControl, int offset) { IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset); return p != null ? p.getContextInformationValidator() : null; } /** * Returns the context information presenter that should be used to display context information. * The position is used to determine the appropriate content assist processor to invoke. * * @param viewer the text viewer * @param offset a document offset * @return a presenter * @since 2.0 */ IContextInformationPresenter getContextInformationPresenter(ITextViewer viewer, int offset) { IContextInformationValidator validator= getContextInformationValidator(viewer, offset); if (validator instanceof IContextInformationPresenter) return (IContextInformationPresenter) validator; return null; } /** * Returns the context information presenter that should be used to display context information. * The position is used to determine the appropriate content assist processor to invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return a presenter * @since 3.0 */ IContextInformationPresenter getContextInformationPresenter(IContentAssistSubjectControl contentAssistSubjectControl, int offset) { IContextInformationValidator validator= getContextInformationValidator(contentAssistSubjectControl, offset); if (validator instanceof IContextInformationPresenter) return (IContextInformationPresenter) validator; return null; } /** * Returns the characters which when typed by the user should automatically initiate proposing * completions. The position is used to determine the appropriate content assist processor to * invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return the auto activation characters * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() * @since 3.0 */ char[] getCompletionProposalAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) { IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset); return p != null ? p.getCompletionProposalAutoActivationCharacters() : null; } /** * Returns the characters which when typed by the user should automatically initiate proposing * completions. The position is used to determine the appropriate content assist processor to * invoke. * * @param viewer the text viewer * @param offset a document offset * @return the auto activation characters * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() */ char[] getCompletionProposalAutoActivationCharacters(ITextViewer viewer, int offset) { IContentAssistProcessor p= getProcessor(viewer, offset); return p != null ? p.getCompletionProposalAutoActivationCharacters() : null; } /** * Returns the characters which when typed by the user should automatically initiate the * presentation of context information. The position is used to determine the appropriate * content assist processor to invoke. * * @param viewer the text viewer * @param offset a document offset * @return the auto activation characters * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters() * @since 3.0 */ char[] getContextInformationAutoActivationCharacters(ITextViewer viewer, int offset) { IContentAssistProcessor p= getProcessor(viewer, offset); return p != null ? p.getContextInformationAutoActivationCharacters() : null; } /** * Returns the characters which when typed by the user should automatically initiate the * presentation of context information. The position is used to determine the appropriate * content assist processor to invoke. * * @param contentAssistSubjectControl the content assist subject control * @param offset a document offset * @return the auto activation characters * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters() * @since 3.0 */ char[] getContextInformationAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) { IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset); return p != null ? p.getContextInformationAutoActivationCharacters() : null; } @Override public boolean requestWidgetToken(IWidgetTokenOwner owner) { return false; } @Override public boolean requestWidgetToken(IWidgetTokenOwner owner, int priority) { if (priority > WIDGET_PRIORITY) { hide(); return true; } return false; } @Override public boolean setFocus(IWidgetTokenOwner owner) { if (fProposalPopup != null) { fProposalPopup.setFocus(); return fProposalPopup.hasFocus(); } return false; } /** * Hides any open pop-ups. * * @since 3.0 */ protected void hide() { if (fProposalPopup != null) fProposalPopup.hide(); if (fContextInfoPopup != null) fContextInfoPopup.hide(); } // ------ control's size handling dialog settings ------ /** * Tells this information control manager to open the information control with the values * contained in the given dialog settings and to store the control's last valid size in the * given dialog settings. *

* Note: This API is only valid if the information control implements * {@link org.eclipse.jface.text.IInformationControlExtension3}. Not following this restriction * will later result in an {@link UnsupportedOperationException}. *

*

* The constants used to store the values are: *

    *
  • {@link ContentAssistant#STORE_SIZE_X}
  • *
  • {@link ContentAssistant#STORE_SIZE_Y}
  • *
  • {@link ContentAssistant#STORE_CONTEXT_SELECTOR_POPUP_SIZE_X}
  • *
  • {@link ContentAssistant#STORE_CONTEXT_SELECTOR_POPUP_SIZE_Y}
  • *
*

* * @param dialogSettings the dialog settings * @since 3.0 */ public void setRestoreCompletionProposalSize(IDialogSettings dialogSettings) { Assert.isTrue(dialogSettings != null); fDialogSettings= dialogSettings; } /** * Stores the content assist's proposal pop-up size. */ protected void storeCompletionProposalPopupSize() { if (fDialogSettings == null || fProposalPopup == null) return; Point size= fProposalPopup.getSize(); if (size == null) return; fDialogSettings.put(STORE_SIZE_X, size.x); fDialogSettings.put(STORE_SIZE_Y, size.y); } /** * Stores the content assist's context selector pop-up size. * * @since 3.9 */ protected void storeContextSelectorPopupSize() { if (fDialogSettings == null || fContextInfoPopup == null) return; Point size= fContextInfoPopup.getContextSelectorPopupSize(); if (size == null) return; fDialogSettings.put(STORE_CONTEXT_SELECTOR_POPUP_SIZE_X, size.x); fDialogSettings.put(STORE_CONTEXT_SELECTOR_POPUP_SIZE_Y, size.y); } /** * Restores the content assist's proposal pop-up size. * * @return the stored size or null if none * @since 3.0 */ protected Point restoreCompletionProposalPopupSize() { if (fDialogSettings == null) return null; Point size= new Point(-1, -1); try { size.x= fDialogSettings.getInt(STORE_SIZE_X); size.y= fDialogSettings.getInt(STORE_SIZE_Y); } catch (NumberFormatException ex) { return null; } // sanity check if (size.x == -1 && size.y == -1) return null; Rectangle maxBounds= null; if (fContentAssistSubjectControl != null && Helper.okToUse(fContentAssistSubjectControl.getControl())) maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds(); else { // fallback Display display= Display.getCurrent(); if (display == null) display= Display.getDefault(); if (display != null && !display.isDisposed()) maxBounds= display.getBounds(); } if (size.x > -1 && size.y > -1) { if (maxBounds != null) { size.x= Math.min(size.x, maxBounds.width); size.y= Math.min(size.y, maxBounds.height); } // Enforce an absolute minimal size size.x= Math.max(size.x, 30); size.y= Math.max(size.y, 30); } return size; } /** * Restores the content assist's context selector pop-up size. * * @return the stored size or null if none * @since 3.9 */ protected Point restoreContextSelectorPopupSize() { if (fDialogSettings == null) return null; Point size= new Point(-1, -1); try { size.x= fDialogSettings.getInt(STORE_CONTEXT_SELECTOR_POPUP_SIZE_X); size.y= fDialogSettings.getInt(STORE_CONTEXT_SELECTOR_POPUP_SIZE_Y); } catch (NumberFormatException ex) { return null; } // sanity check if (size.x == -1 && size.y == -1) return null; Rectangle maxBounds= null; Display display= Display.getCurrent(); if (display == null) display= Display.getDefault(); if (display != null && !display.isDisposed()) maxBounds= display.getBounds(); if (size.x > -1 && size.y > -1) { if (maxBounds != null) { size.x= Math.min(size.x, maxBounds.width); size.y= Math.min(size.y, maxBounds.height); } // Enforce an absolute minimal size size.x= Math.max(size.x, 30); size.y= Math.max(size.y, 30); } return size; } /** * Sets the prefix completion property. If enabled, content assist delegates completion to * prefix completion. * * @param enabled true to enable prefix completion, false to * disable */ public void enablePrefixCompletion(boolean enabled) { fIsPrefixCompletionEnabled= enabled; } /** * Returns the prefix completion state. * * @return true if prefix completion is enabled, false otherwise * @since 3.2 */ boolean isPrefixCompletionEnabled() { return fIsPrefixCompletionEnabled; } /** * Returns whether the content assistant proposal popup has the focus. * * @return true if the proposal popup has the focus * @since 3.0 */ public boolean hasProposalPopupFocus() { return fProposalPopup.hasFocus(); } @Override public void addCompletionListener(ICompletionListener listener) { Assert.isLegal(listener != null); fCompletionListeners.add(listener); } @Override public void removeCompletionListener(ICompletionListener listener) { fCompletionListeners.remove(listener); } /** * Fires a session begin event to all registered {@link ICompletionListener}s. * * @param isAutoActivated true if this session was triggered by auto activation * @since 3.2 */ void fireSessionBeginEvent(boolean isAutoActivated) { if (fContentAssistSubjectControlAdapter != null && !isProposalPopupActive()) { IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x); ContentAssistEvent event= new ContentAssistEvent(this, processor, isAutoActivated); for (ICompletionListener listener : fCompletionListeners) { listener.assistSessionStarted(event); } } } /** * Fires a session restart event to all registered {@link ICompletionListener}s. * * @since 3.4 */ void fireSessionRestartEvent() { if (fContentAssistSubjectControlAdapter != null) { IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x); ContentAssistEvent event= new ContentAssistEvent(this, processor); for (ICompletionListener listener : fCompletionListeners) { if (listener instanceof ICompletionListenerExtension) ((ICompletionListenerExtension)listener).assistSessionRestarted(event); } } } /** * Fires a session end event to all registered {@link ICompletionListener}s. * * @since 3.2 */ void fireSessionEndEvent() { if (fContentAssistSubjectControlAdapter != null) { IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x); ContentAssistEvent event= new ContentAssistEvent(this, processor); for (ICompletionListener listener : fCompletionListeners) { listener.assistSessionEnded(event); } } } @Override public void setRepeatedInvocationMode(boolean cycling) { fIsRepetitionMode= cycling; } /** * Returns true if repeated invocation mode is enabled, false * otherwise. * * @return true if repeated invocation mode is enabled, false * otherwise * @since 3.2 */ boolean isRepeatedInvocationMode() { return fIsRepetitionMode; } @Override public void setShowEmptyList(boolean showEmpty) { fShowEmptyList= showEmpty; } /** * Returns true if empty lists should be displayed, false * otherwise. * * @return true if empty lists should be displayed, false * otherwise * @since 3.2 */ boolean isShowEmptyList() { return fShowEmptyList; } @Override public void setStatusLineVisible(boolean show) { fIsStatusLineVisible= show; if (fProposalPopup != null) fProposalPopup.setStatusLineVisible(show); } /** * Returns true if a message line should be displayed, false * otherwise. * * @return true if a message line should be displayed, false * otherwise * @since 3.2 */ boolean isStatusLineVisible() { return fIsStatusLineVisible; } @Override public void setStatusMessage(String message) { Assert.isLegal(message != null); fMessage= message; if (fProposalPopup != null) fProposalPopup.setMessage(message); } /** * Returns the affordance caption for the cycling affordance at the bottom of the pop-up. * * @return the affordance caption for the cycling affordance at the bottom of the pop-up * @since 3.2 */ String getStatusMessage() { return fMessage; } @Override public void setEmptyMessage(String message) { Assert.isLegal(message != null); if (fProposalPopup != null) fProposalPopup.setEmptyMessage(message); } /** * Fires a selection event, see {@link ICompletionListener}. * * @param proposal the selected proposal, possibly null * @param smartToggle true if the smart toggle is on * @since 3.2 */ void fireSelectionEvent(ICompletionProposal proposal, boolean smartToggle) { for (ICompletionListener listener : fCompletionListeners) { listener.selectionChanged(proposal, smartToggle); } } /** * Fires an event after applying a proposal, see {@link ICompletionListenerExtension2}. * * @param proposal the applied proposal * @since 3.8 */ void fireAppliedEvent(ICompletionProposal proposal) { for (ICompletionListener listener : fCompletionListeners) { if (listener instanceof ICompletionListenerExtension2) ((ICompletionListenerExtension2)listener).applied(proposal); } } /* * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(org.eclipse.jface.bindings.keys.KeySequence) * @since 3.2 */ @Override public void setRepeatedInvocationTrigger(KeySequence sequence) { fRepeatedInvocationKeySequence= sequence; } /** * Returns the repeated invocation key sequence. * * @return the repeated invocation key sequence or null, if none * @since 3.2 */ KeySequence getRepeatedInvocationKeySequence() { return fRepeatedInvocationKeySequence; } /** * Returns whether proposal popup is active. * * @return true if the proposal popup is active, false otherwise * @since 3.4 */ protected boolean isProposalPopupActive(){ return fProposalPopup != null && fProposalPopup.isActive(); } /** * Returns whether the context information popup is active. * * @return true if the context information popup is active, false otherwise * @since 3.4 */ protected boolean isContextInfoPopupActive(){ return fContextInfoPopup != null && fContextInfoPopup.isActive(); } /** * {@inheritDoc} * * @since 3.4 */ @Override public final IHandler getHandler(String commandId) { if (fHandlers == null) throw new IllegalStateException(); IHandler handler= fHandlers.get(commandId); if (handler != null) return handler; Assert.isLegal(false); return null; } /** * Registers the given handler under the given command identifier. * * @param commandId the command identifier * @param handler the handler * @since 3.4 */ protected final void registerHandler(String commandId, IHandler handler) { if (fHandlers == null) fHandlers= new HashMap<>(2); fHandlers.put(commandId, handler); } /** * Tells whether the support for colored labels is enabled. * * @return true if the support for colored labels is enabled, false otherwise * @since 3.4 */ boolean isColoredLabelsSupportEnabled() { return fIsColoredLabelsSupportEnabled; } /** * Enables the support for colored labels in the proposal popup. *

Completion proposals can implement {@link ICompletionProposalExtension6} * to provide colored proposal labels.

* * @param isEnabled if true the support for colored labels is enabled in the proposal popup * @since 3.4 */ public void enableColoredLabels(boolean isEnabled) { fIsColoredLabelsSupportEnabled= isEnabled; } /** * Sets the proposal sorter. * * @param sorter the sorter to be used, or null if no sorting is requested * @since 3.8 */ public void setSorter(ICompletionProposalSorter sorter) { fSorter= sorter; if (fProposalPopup != null) { fProposalPopup.setSorter(fSorter); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy