blob: 0f9802666c9e564551a89c60ee795d2699cef134 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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
*
*******************************************************************************/
package org.eclipse.dltk.ui.text.completion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.PreferenceConstants;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationExtension;
import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* Computes Script completion proposals and context infos.
*/
public abstract class ScriptCompletionProposalComputer extends AbstractScriptCompletionProposalComputer
implements IScriptCompletionProposalComputer {
private static final class ContextInformationWrapper implements IContextInformation, IContextInformationExtension {
private final IContextInformation fContextInformation;
private int fPosition;
public ContextInformationWrapper(IContextInformation contextInformation) {
fContextInformation = contextInformation;
}
@Override
public String getContextDisplayString() {
return fContextInformation.getContextDisplayString();
}
@Override
public Image getImage() {
return fContextInformation.getImage();
}
@Override
public String getInformationDisplayString() {
return fContextInformation.getInformationDisplayString();
}
@Override
public int getContextInformationPosition() {
return fPosition;
}
public void setContextInformationPosition(int position) {
fPosition = position;
}
@Override
public boolean equals(Object object) {
if (object instanceof ContextInformationWrapper) {
return fContextInformation.equals(((ContextInformationWrapper) object).fContextInformation);
}
return fContextInformation.equals(object);
}
}
private String fErrorMessage;
private List<IContextInformation> addContextInformations(ScriptContentAssistInvocationContext context, int offset,
IProgressMonitor monitor) {
List<ICompletionProposal> proposals = computeScriptCompletionProposals(offset, context, monitor);
List<IContextInformation> result = new ArrayList<>(proposals.size());
for (ICompletionProposal proposal : proposals) {
IContextInformation contextInformation = proposal.getContextInformation();
if (contextInformation != null) {
ContextInformationWrapper wrapper = new ContextInformationWrapper(contextInformation);
wrapper.setContextInformationPosition(offset);
result.add(wrapper);
}
}
return result;
}
private void handleCodeCompletionException(ModelException e, ScriptContentAssistInvocationContext context) {
ISourceModule module = context.getSourceModule();
Runnable openDialog = () -> {
Shell shell = context.getViewer().getTextWidget().getShell();
if (e.isDoesNotExist() && !module.getScriptProject().isOnBuildpath(module)) {
IPreferenceStore store = DLTKUIPlugin.getDefault().getPreferenceStore();
boolean value = store.getBoolean(PreferenceConstants.NOTIFICATION_NOT_ON_BUILDPATH_MESSAGE);
if (!value) {
MessageDialog.openInformation(shell,
ScriptTextMessages.CompletionProcessor_error_notOnBuildPath_title,
ScriptTextMessages.CompletionProcessor_error_notOnBuildPath_message);
}
store.setValue(PreferenceConstants.NOTIFICATION_NOT_ON_BUILDPATH_MESSAGE, true);
} else {
ErrorDialog.openError(shell, ScriptTextMessages.CompletionProcessor_error_accessing_title,
ScriptTextMessages.CompletionProcessor_error_accessing_message, e.getStatus());
}
};
if (Display.getCurrent() != null) {
openDialog.run();
} else {
Display.getDefault().asyncExec(openDialog);
}
}
// Code template completion proposals for script language
protected List<ICompletionProposal> computeTemplateCompletionProposals(int offset,
ScriptContentAssistInvocationContext context, IProgressMonitor monitor) {
TemplateCompletionProcessor templateProcessor = createTemplateProposalComputer(context);
if (templateProcessor != null) {
ICompletionProposal[] proposals = templateProcessor.computeCompletionProposals(context.getViewer(), offset);
if (proposals != null && proposals.length != 0) {
updateTemplateProposalRelevance(context, proposals);
}
return Arrays.asList(proposals);
}
return Collections.emptyList();
}
// Script language specific completion proposals like types or keywords
protected List<ICompletionProposal> computeScriptCompletionProposals(int offset,
ScriptContentAssistInvocationContext context, IProgressMonitor monitor) {
// Source module getting
ISourceModule sourceModule = context.getSourceModule();
if (sourceModule == null) {
return Collections.emptyList();
}
// Create and configure collector
ScriptCompletionProposalCollector collector = createCollector(context);
if (collector == null) {
return Collections.emptyList();
}
collector.setInvocationContext(context);
Runnable collectLength = () -> {
Point selection = context.getViewer().getSelectedRange();
if (selection.y > 0) {
collector.setReplacementLength(selection.y);
}
};
if (Display.getCurrent() != null) {
collectLength.run();
} else {
Display.getDefault().syncExec(collectLength);
}
// Filling collector with proposals
try {
if (DLTKCore.DEBUG_COMPLETION) {
IModelElement element = sourceModule.getElementAt(offset);
if (element != null) {
System.out.println("========= Model element: " //$NON-NLS-1$
+ element.getClass());
}
}
int timeout = DLTKUIPlugin.getDefault().getPreferenceStore().getInt(PreferenceConstants.CODEASSIST_TIMEOUT);
collector.startCompletion();
sourceModule.codeComplete(offset, collector, timeout);
collector.endCompletion();
} catch (ModelException e) {
handleCodeCompletionException(e, context);
}
ICompletionProposal[] proposals = collector.getScriptCompletionProposals();
// Checking proposals
if (proposals.length == 0) {
String error = collector.getErrorMessage();
if (error != null && error.length() > 0) {
fErrorMessage = error;
}
return Collections.emptyList();
}
return Arrays.asList(proposals);
}
public ScriptCompletionProposalComputer() {
}
/*
* @seeorg.eclipse.jface.text.contentassist.ICompletionProposalComputer#
* computeContextInformation
* (org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext,
* org.eclipse.core.runtime.IProgressMonitor)
*/
/*
* public List computeContextInformation(ContentAssistInvocationContext context,
* IProgressMonitor monitor) { if (context instanceof
* ScriptContentAssistInvocationContext) { ScriptContentAssistInvocationContext
* scriptContext= (ScriptContentAssistInvocationContext) context;
*
* int contextInformationPosition=
* guessContextInformationPosition(scriptContext); List result=
* addContextInformations(scriptContext, contextInformationPosition, monitor);
* return result; } return Collections.EMPTY_LIST; }
*/
// Completion proposals
@Override
public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context,
IProgressMonitor monitor) {
if (context instanceof ScriptContentAssistInvocationContext) {
ScriptContentAssistInvocationContext scriptContext = (ScriptContentAssistInvocationContext) context;
List<ICompletionProposal> proposals = new ArrayList<>();
// Language specific proposals (already sorted and etc.)
proposals.addAll(computeScriptCompletionProposals(context.getInvocationOffset(), scriptContext, monitor));
// Template proposals (already sorted and etc.)
proposals.addAll(computeTemplateCompletionProposals(context.getInvocationOffset(), scriptContext, monitor));
return proposals;
}
return Collections.emptyList();
}
protected int guessContextInformationPosition(ContentAssistInvocationContext context) {
return context.getInvocationOffset();
}
@Override
public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context,
IProgressMonitor monitor) {//
if (context instanceof ScriptContentAssistInvocationContext) {
ScriptContentAssistInvocationContext scriptContext = (ScriptContentAssistInvocationContext) context;
int contextInformationPosition = guessContextInformationPosition(scriptContext);
List<IContextInformation> result = addContextInformations(scriptContext, contextInformationPosition,
monitor);
return result;
}
return Collections.emptyList();
// List types = computeCompletionProposals(context, monitor);
// Iterator iter = types.iterator();
// List list = new ArrayList();
// while (iter.hasNext()) {
// Object o = iter
// .next();
// if( !( o instanceof IScriptCompletionProposal ) ) {
// continue;
// }
// IScriptCompletionProposal proposal = (IScriptCompletionProposal) o;
// // System.out.println("Proposal: " + proposal + ", info: "
// // + proposal.getContextInformation());
// // System.out.println(proposal.getClass());
// list.add(proposal.getContextInformation());
// }
// return list;
}
@Override
public String getErrorMessage() {
return fErrorMessage;
}
@Override
public void sessionStarted() {
}
@Override
public void sessionEnded() {
fErrorMessage = null;
}
/**
* Creates the template completion processor
*
* <p>
* Subclasses may return <code>null</code> if they do not wish to provide
* template support.
* </p>
*/
protected TemplateCompletionProcessor createTemplateProposalComputer(ScriptContentAssistInvocationContext context) {
return null;
}
protected abstract ScriptCompletionProposalCollector createCollector(ScriptContentAssistInvocationContext context);
}