blob: 29586bffc012965dae489354aeff26e2afa7bdc4 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2020 IBM Corporation and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# IBM Corporation - org.eclipse.jdt: initial API and implementation
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.sourceediting.assist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import com.ibm.icu.text.Collator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ContentAssistEvent;
import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionListenerExtension;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.ui.SharedMessages;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.ltk.ui.EditingMessages;
import org.eclipse.statet.ltk.core.Ltk;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
/**
* A content assist processor that aggregates the proposals of the {@link ContentAssistComputer}s
* contributed via the extension point <code>org.eclipse.statet.ltk.advancedContentAssist</code>.
* <p>
* Subclasses may extend:
* <ul>
* <li><code>createContext</code> to provide the context object passed to the computers</li>
* <li><code>createProgressMonitor</code> to change the way progress is reported</li>
* <li><code>filterAndSort</code> to add sorting and filtering</li>
* <li><code>getContextInformationValidator</code> to add context validation (needed if any
* contexts are provided)</li>
* <li><code>getErrorMessage</code> to change error reporting</li>
* </ul></p>
*/
@NonNullByDefault
public class ContentAssistProcessor extends ContentAssist.Processor implements IContentAssistProcessor {
private static final boolean DEBUG_LOG= Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.statet.ltk/debug/ContentAssist/log") ); //$NON-NLS-1$
private static final Collator NAME_COLLATOR= Collator.getInstance();
static final Comparator<AssistProposal> PROPOSAL_COMPARATOR= new Comparator<AssistProposal>() {
@Override
public int compare(final AssistProposal proposal1, final AssistProposal proposal2) {
final int diff= proposal2.getRelevance() - proposal1.getRelevance();
if (diff != 0) {
return diff; // reverse
}
return NAME_COLLATOR.compare(proposal1.getSortingString(), proposal2.getSortingString());
}
};
private static final long INFO_ITER_SPAN= 3L * 1000000000L;
/**
* The completion listener class for this processor.
*/
private final class CompletionListener implements ICompletionListener, ICompletionListenerExtension {
@Override
public void assistSessionStarted(final ContentAssistEvent event) {
if (event.processor == ContentAssistProcessor.this) {
startCompletionSession();
}
}
@Override
public void assistSessionRestarted(final ContentAssistEvent event) {
if (isInCompletionProposalSession()) {
if (event.processor == ContentAssistProcessor.this) {
restartCompletionSession();
}
else {
endCompletionSession();
}
}
}
@Override
public void assistSessionEnded(final ContentAssistEvent event) {
if (isInCompletionProposalSession()) {
endCompletionSession();
}
}
@Override
public void selectionChanged(final @Nullable ICompletionProposal proposal, final boolean smartToggle) {
}
}
/**
* The completion proposal registry.
*/
private final ContentAssistComputerRegistry computerRegistry;
private final ISourceEditor editor;
private char[] completionProposalsAutoActivationCharacters;
private char[] contextInformationAutoActivationCharacters;
private boolean isCompletionProposalSession;
private int sessionCounter;
/* cycling stuff */
private int iterationPos= -1;
private boolean noIteration;
private final CopyOnWriteIdentityListSet<ContentAssistCategory> expliciteCategories= new CopyOnWriteIdentityListSet<>();
private List<ContentAssistCategory> availableCategories;
private List<List<ContentAssistCategory>> categoryIteration;
private @Nullable AssistInvocationContext completionProposalContext;
private @Nullable IContextInformationValidator contextInformationValidator;
/* for detection if information mode is valid */
private long informationModeTimestamp;
private long informationModeModificationStamp;
private int informationModeOffset;
private ICompletionProposal @Nullable [] reloadCompletionProposals;
public ContentAssistProcessor(final ContentAssist assistant, final String contentType,
final ContentAssistComputerRegistry registry, final ISourceEditor editor) {
super(assistant, contentType);
assert(registry != null);
assert(editor != null);
this.computerRegistry= registry;
this.editor= editor;
getAssistant().enableColoredLabels(true);
getAssistant().addCompletionListener(new CompletionListener());
}
protected ISourceEditor getEditor() {
return this.editor;
}
protected final boolean isInCompletionProposalSession() {
return this.isCompletionProposalSession;
}
protected final int getSessionCounter() {
return this.sessionCounter;
}
public void addCategory(final ContentAssistCategory category) {
this.expliciteCategories.add(category);
}
@Override
protected ICompletionProposal @Nullable [] doComputeCompletionProposals(
final ITextViewer viewer, final int offset) {
if (!isInCompletionProposalSession()) {
startCompletionSession();
}
clearState();
if (this.reloadCompletionProposals != null) {
return this.reloadCompletionProposals;
}
return computeCompletionProposals(offset);
}
private final ICompletionProposal @Nullable [] computeCompletionProposals(final int offset) {
final ContentAssist assistant= getAssistant();
final long startTime= System.nanoTime();
final SubMonitor m= SubMonitor.convert(createProgressMonitor());
m.beginTask(EditingMessages.ContentAssistProcessor_ComputingProposals_task, 3 + 10 + 1);
try {
final AssistInvocationContext context= getCompletionProposalContext(offset, m.newChild(3));
final long setup= (DEBUG_LOG) ? System.nanoTime() : 0;
final long modificationStamp= ((AbstractDocument) context.getSourceViewer().getDocument()).getModificationStamp();
final int mode;
if (assistant.isCompletionProposalSpecificSession()) {
mode= ContentAssistComputer.SPECIFIC_MODE;
}
else if (!assistant.isProposalPopupActive1()
&& ( startTime - this.informationModeTimestamp > INFO_ITER_SPAN
|| offset != this.informationModeOffset
|| !assistant.isContextInfoPopupActive1()
|| modificationStamp != this.informationModeModificationStamp)
&& forceContextInformation(context)) {
assistant.setRepeatedInvocationMode(true);
assistant.setShowEmptyList(true);
assistant.enableAutoInsertTemporarily();
assistant.setStatusLineVisible(true);
assistant.setStatusMessage(EditingMessages.ContentAssistProcessor_ContextSelection_label);
return computeContextInformationProposals(context, modificationStamp, m.newChild(10) );
}
else {
iterate();
mode= (this.iterationPos == 0) ? ContentAssistComputer.COMBINED_MODE : ContentAssistComputer.SPECIFIC_MODE;
assistant.enableAutoInsertSetting();
}
final List<ContentAssistCategory> categories= getCurrentCategories();
m.subTask(EditingMessages.ContentAssistProcessor_ComputingProposals_Collecting_task);
final AssistProposalCollector proposals= createProposalCollector();
collectCompletionProposals(context, mode, categories, proposals, m.newChild(10));
final long collect= (DEBUG_LOG) ? System.nanoTime() : 0;
m.subTask(EditingMessages.ContentAssistProcessor_ComputingProposals_Sorting_task);
final AssistProposal[] result= filterAndSortCompletionProposals(proposals,
context, m.newChild(1) );
final long filter= (DEBUG_LOG) ? System.nanoTime() : 0;
if (DEBUG_LOG) {
final StringBuilder sb= new StringBuilder("Code Assist Stats"); //$NON-NLS-1$
sb.append(" (").append(result.length).append(" proposals)"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "setup= ").append((setup - startTime)); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "collect= ").append((collect - setup)); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "sort= ").append((filter - collect)); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println(sb);
}
return result;
}
finally {
m.done();
}
}
private void startCompletionSession() {
this.isCompletionProposalSession= true;
this.sessionCounter++;
final ContentAssist assistant= getAssistant();
this.availableCategories= assistant.isCompletionProposalSpecificSession() ?
this.computerRegistry.getCategory(assistant.getSpecificMode()) :
this.computerRegistry.getCategories();
this.categoryIteration= createCategoryIteration();
notifySessionStarted();
this.iterationPos= -1;
if (this.categoryIteration.size() == 1) {
assistant.setRepeatedInvocationMode(false);
assistant.setShowEmptyList(false);
}
else {
assistant.setRepeatedInvocationMode(true);
assistant.setShowEmptyList(true);
assistant.setStatusLineVisible(true);
assistant.setStatusMessage(createIterationMessage(0));
}
}
private void restartCompletionSession() {
assert(this.isCompletionProposalSession);
if (this.iterationPos >= 0) {
this.iterationPos--;
}
this.completionProposalContext= null;
}
private void endCompletionSession() {
assert(this.isCompletionProposalSession);
this.isCompletionProposalSession= false;
final ContentAssist assistant= getAssistant();
notifySessionEnded();
this.availableCategories= null;
this.categoryIteration= null;
this.iterationPos= -1;
assistant.setRepeatedInvocationTrigger(null);
assistant.setRepeatedInvocationMode(false);
assistant.setShowEmptyList(false);
assistant.enableAutoInsertSetting();
assistant.setStatusLineVisible(false);
this.completionProposalContext= null;
}
private void clearState() {
// this.status= null;
}
/**
* Collects the proposals.
*
* @param context the code assist invocation context
* @param mode
* @param categories list of categories to use
* @param proposals collector for the proposals
* @param monitor the progress monitor
* @return the list of proposals
*/
private boolean collectCompletionProposals(
final AssistInvocationContext context, final int mode,
final List<ContentAssistCategory> categories,
final AssistProposalCollector proposals, final SubMonitor m) {
m.setWorkRemaining(categories.size());
for (final ContentAssistCategory category : categories) {
final List<ContentAssistComputer> computers= category.getComputers(getContentType());
final SubMonitor m1= m.newChild(1);
for (final ContentAssistComputer computer : computers) {
try {
computer.computeCompletionProposals(context, mode, proposals, m1);
}
catch (final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, Ltk.BUNDLE_ID,
"An error occurred when computing content assistant completion proposals.",
e ));
}
}
}
return true;
}
@Override
protected IContextInformation @Nullable [] doComputeContextInformation(
final ITextViewer viewer, final int offset) {
clearState();
final SubMonitor m= SubMonitor.convert(createProgressMonitor(),
EditingMessages.ContentAssistProcessor_ComputingContexts_task, 3 + 10 + 1);
try {
if (!this.isCompletionProposalSession) {
this.sessionCounter++;
this.availableCategories= this.computerRegistry.getCategories();
notifySessionStarted();
}
final AssistInvocationContext context= getContextInformationContext(offset, m.newChild(3));
final long modificationStamp= (!getAssistant().isContextInformationAutoRequest()) ?
((AbstractDocument) context.getSourceViewer().getDocument()).getModificationStamp() :
0;
final AssistProposal[] proposals= computeContextInformationProposals(
context, modificationStamp, m.newChild(10) );
return (proposals != null) ? toInformationProposals(proposals, context) : null;
}
finally {
if (!this.isCompletionProposalSession) {
notifySessionEnded();
this.availableCategories= null;
}
m.done();
}
}
private AssistProposal @Nullable [] computeContextInformationProposals(
final AssistInvocationContext context,
final long modificationStamp,
final SubMonitor m) {
m.setWorkRemaining(10 + 1);
m.subTask(EditingMessages.ContentAssistProcessor_ComputingContexts_Collecting_task);
final ContentAssist assistant= getAssistant();
final List<ContentAssistCategory> defaultGroup= new ArrayList<>();
final List<ContentAssistCategory> otherGroup= new ArrayList<>();
for (final ContentAssistCategory category : this.availableCategories) {
if (category.isEnabledInDefault()) {
defaultGroup.add(category);
}
else if (category.isEnabledInCircling()) {
otherGroup.add(category);
}
}
boolean ok;
final AssistProposalCollector proposals= createProposalCollector();
if (modificationStamp == 0) {
ok= false;
if (collectInformationProposals(context, defaultGroup, true, proposals,
m.newChild(5) )) {
if (proposals.getCount() == 1
|| (collectInformationProposals(context, otherGroup, true, proposals,
m.newChild(5) )
&& proposals.getCount() == 1) ) {
ok= true;
}
}
}
else {
ok= true;
collectInformationProposals(context, defaultGroup, false, proposals,
m.newChild(5) );
if (proposals.getCount() == 0) {
collectInformationProposals(context, otherGroup, false, proposals,
m.newChild(5) );
}
}
if (!ok) {
return null;
}
m.setWorkRemaining(1);
m.subTask(EditingMessages.ContentAssistProcessor_ComputingContexts_Sorting_task);
final AssistProposal[] result= filterAndSortCompletionProposals(proposals,
context, m.newChild(1) );
if (modificationStamp != 0) {
this.informationModeOffset= context.getInvocationOffset();
this.informationModeTimestamp= System.nanoTime();
this.informationModeModificationStamp= modificationStamp;
}
return result;
}
/**
* @return <code>false</code> if cancelled
*/
private boolean collectInformationProposals(final AssistInvocationContext context,
final List<ContentAssistCategory> categories, final boolean single,
final AssistProposalCollector proposals, final SubMonitor m) {
m.setWorkRemaining(categories.size());
for (final ContentAssistCategory category : categories) {
final List<ContentAssistComputer> computers= category.getComputers(getContentType());
final SubMonitor m1= m.newChild(1);
for (final ContentAssistComputer computer : computers) {
computer.onSessionStarted(context.getEditor(), getAssistant());
try {
computer.computeInformationProposals(context, proposals, m1);
}
catch (final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, Ltk.BUNDLE_ID,
"An error occurred when computing content assistant context information.",
e ));
}
finally {
computer.onSessionEnded();
}
if ((single && proposals.getCount() > 1)) {
return false;
}
}
}
return true;
}
protected AssistProposalCollector createProposalCollector() {
return new AssistProposalCollector();
}
/**
* Filters and sorts the proposals. The passed list may be modified
* and returned, or a new list may be created and returned.
*
* @param proposals the list of collected proposals
* @param context
* @param monitor a progress monitor
* @return the list of filtered and sorted proposals, ready for display
*/
protected AssistProposal[] filterAndSortCompletionProposals(
final AssistProposalCollector proposals,
final AssistInvocationContext context, final SubMonitor m) {
final AssistProposal[] array= proposals.toArray();
if (array.length > 1) {
Arrays.sort(array, PROPOSAL_COMPARATOR);
}
return array;
}
protected AssistInformationProposal @Nullable [] toInformationProposals(
final AssistProposal[] proposals, final AssistInvocationContext context) {
final AssistInformationProposal[] infoProposals=
new @NonNull AssistInformationProposal[proposals.length];
for (int i= 0; i < proposals.length; i++) {
final AssistProposal proposal= proposals[i];
infoProposals[i]= (proposal instanceof AssistInformationProposal) ?
(AssistInformationProposal) proposal :
new AssistCompletionInformationProposalWrapper(proposal, context);
}
return infoProposals;
}
/**
* Sets this processor's set of characters triggering the activation of the
* completion proposal computation.
*
* @param activationSet the activation set
*/
public final void setCompletionProposalAutoActivationCharacters(final char[] activationSet) {
this.completionProposalsAutoActivationCharacters= activationSet;
}
@Override
public final char[] getCompletionProposalAutoActivationCharacters() {
return this.completionProposalsAutoActivationCharacters;
}
/**
* Sets this processor's set of characters triggering the activation of the
* completion proposal computation.
*
* @param activationSet the activation set
*/
public final void setContextInformationAutoActivationCharacters(final char[] activationSet) {
this.contextInformationAutoActivationCharacters= activationSet;
}
@Override
public char[] getContextInformationAutoActivationCharacters() {
return this.contextInformationAutoActivationCharacters;
}
@Override
public @Nullable String getErrorMessage() {
// final IStatus status= this.status;
// return (status != null && status.getSeverity() == IStatus.ERROR) ? status.getMessage() : null;
return null;
}
/**
* {@inheritDoc}
*
* This implementation returns the validator created by
* {@link #createContextInformationValidator()}
*/
@Override
public final @Nullable IContextInformationValidator getContextInformationValidator() {
IContextInformationValidator contextInformationValidator= this.contextInformationValidator;
if (contextInformationValidator == null) {
contextInformationValidator= createContextInformationValidator();
this.contextInformationValidator= contextInformationValidator;
}
return contextInformationValidator;
}
protected @Nullable IContextInformationValidator createContextInformationValidator() {
return null;
}
/**
* Creates a progress monitor.
* <p>
* The default implementation creates a
* <code>NullProgressMonitor</code>.
* </p>
*
* @return a progress monitor
*/
protected IProgressMonitor createProgressMonitor() {
return new NullProgressMonitor();
}
private AssistInvocationContext getCompletionProposalContext(final int offset,
final IProgressMonitor monitor) {
AssistInvocationContext context= this.completionProposalContext;
if (context == null || !context.reuse(getEditor(), offset)) {
context= createCompletionProposalContext(offset, monitor);
this.completionProposalContext= context;
}
context.session= this.sessionCounter;
return context;
}
/**
* Creates the context that is passed to the completion proposal
* computers.
*
* @param offset the content assist offset
* @return the context to be passed to the computers
*/
protected AssistInvocationContext createCompletionProposalContext(final int offset,
final IProgressMonitor monitor) {
return new AssistInvocationContext(getEditor(), offset, getContentType(), 0, monitor);
}
private AssistInvocationContext getContextInformationContext(final int offset,
final SubMonitor m) {
AssistInvocationContext context= this.completionProposalContext;
if (context == null || !context.reuse(getEditor(), offset)) {
context= createContextInformationContext(offset, m);
}
return context;
}
/**
* Creates the context that is passed to the completion proposal
* computers.
*
* @param offset the content assist offset
* @return the context to be passed to the computers
*/
protected AssistInvocationContext createContextInformationContext(final int offset,
final IProgressMonitor monitor) {
return new AssistInvocationContext(getEditor(), offset, getContentType(), 0, monitor);
}
protected boolean forceContextInformation(final AssistInvocationContext context) {
return false;
}
private void iterate() {
if (this.categoryIteration == null) {
this.noIteration= false;
return;
}
if (this.noIteration) {
if (this.iterationPos < 0) {
this.iterationPos= 0;
}
this.noIteration= false;
return;
}
this.iterationPos++;
if (this.iterationPos >= this.categoryIteration.size()) {
this.iterationPos= 0;
}
}
private List<ContentAssistCategory> getCurrentCategories() {
if (this.categoryIteration == null) {
return this.availableCategories;
}
final int iterationPos= this.iterationPos % this.categoryIteration.size();
final ContentAssist assistant= getAssistant();
assistant.setStatusMessage(createIterationMessage(iterationPos));
assistant.setEmptyMessage(createEmptyMessage(iterationPos));
return this.categoryIteration.get(iterationPos);
}
private List<List<ContentAssistCategory>> createCategoryIteration() {
final List<List<ContentAssistCategory>> sequence= new ArrayList<>(this.availableCategories.size());
sequence.add(createDefaultCategories());
for (final ContentAssistCategory category : createSeparateCategories()) {
sequence.add(ImCollections.newList(category));
}
return sequence;
}
private List<ContentAssistCategory> createDefaultCategories() {
final List<ContentAssistCategory> included= new ArrayList<>(this.availableCategories.size());
for (final ContentAssistCategory category : this.availableCategories) {
if (category.isEnabledInDefault() && category.hasComputers(getContentType())) {
included.add(category);
}
}
for (final ContentAssistCategory category : this.expliciteCategories.toList()) {
if (category.isEnabledInDefault() && category.hasComputers(getContentType())) {
included.add(category);
}
}
return included;
}
private List<ContentAssistCategory> createSeparateCategories() {
final ArrayList<ContentAssistCategory> sorted= new ArrayList<>(this.availableCategories.size());
for (final ContentAssistCategory category : this.availableCategories) {
if (category.isEnabledInCircling() && category.hasComputers(getContentType())) {
sorted.add(category);
}
}
return sorted;
}
private void notifySessionStarted() {
for (final ContentAssistCategory category : this.availableCategories) {
final List<ContentAssistComputer> computers= category.getComputers(getContentType());
for (final ContentAssistComputer computer : computers) {
computer.onSessionStarted(ContentAssistProcessor.this.editor, getAssistant());
}
}
}
private void notifySessionEnded() {
if (this.availableCategories == null) {
return;
}
for (final ContentAssistCategory category : this.availableCategories) {
final List<ContentAssistComputer> computers= category.getComputers(getContentType());
for (final ContentAssistComputer computer : computers) {
try {
computer.onSessionEnded();
}
catch (final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, Ltk.BUNDLE_ID, 0,
"Error by contributed content assist computer.", e ));
}
}
}
}
protected String createEmptyMessage(final int iterationPosition) {
return NLS.bind(EditingMessages.ContentAssistProcessor_Empty_message, new String[] {
getCategoryName(iterationPosition)});
}
protected String createIterationMessage(final int iterationPosition) {
// if (iterationPosition >= 0 && iterationPosition == iterationPosition % this.categoryIteration.size()) {
// return getCategoryName(iterationPosition);
// }
final StringBuilder sb= new StringBuilder(
getCategoryName(iterationPosition) );
if (this.categoryIteration.size() > 0) {
sb.append("\u2004\u2004"); //$NON-NLS-1$
sb.append(NLS.bind(SharedMessages.DoToShow_message,
getAssistant().getCompletionProposalIterationGesture(),
getCategoryName(iterationPosition + 1) ));
}
return sb.toString();
}
protected String getCategoryName(final int repetition) {
if (repetition < 0) {
return EditingMessages.ContentAssistProcessor_ContextSelection_label;
}
final int iterationPosition= repetition % this.categoryIteration.size();
if (iterationPosition == 0) {
return EditingMessages.ContentAssistProcessor_DefaultProposalCategory;
}
return this.categoryIteration.get(iterationPosition).get(0).getDisplayName();
}
protected void reloadPossibleCompletions(final AssistInvocationContext context) {
if (isInCompletionProposalSession()
&& context.session == getSessionCounter()) {
UIAccess.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
final SourceViewer viewer= context.getSourceViewer();
if (isInCompletionProposalSession()
&& context.session == getSessionCounter()
&& UIAccess.isOkToUse(viewer)
&& viewer.getTextWidget().isFocusControl()
&& context.isInitialState()
&& getAssistant().getCompletionProposalSelectionCounter() <= 1 ) {
ContentAssistProcessor.this.noIteration= true;
try {
ContentAssistProcessor.this.reloadCompletionProposals=
computeCompletionProposals(context.getInvocationOffset());
if (ContentAssistProcessor.this.reloadCompletionProposals != null
&& ContentAssistProcessor.this.reloadCompletionProposals.length > 0 ) {
getAssistant().reloadPossibleCompletions();
}
}
finally {
ContentAssistProcessor.this.noIteration= false;
ContentAssistProcessor.this.reloadCompletionProposals= null;
}
}
}
});
}
}
}