blob: fc19a40cf218cb9707080f8139c8ada1e30e4e1b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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.codeassist;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.dltk.ast.declarations.Argument;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.compiler.CharOperation;
import org.eclipse.dltk.compiler.problem.CategorizedProblem;
import org.eclipse.dltk.core.CompletionProposal;
import org.eclipse.dltk.core.CompletionRequestor;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IAccessRule;
import org.eclipse.dltk.core.IField;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISearchableEnvironment;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.codeassist.impl.Engine;
import org.eclipse.dltk.internal.compiler.lookup.LookupEnvironment;
public abstract class ScriptCompletionEngine extends Engine implements
ICompletionEngine {
protected static boolean DEBUG = DLTKCore.DEBUG_COMPLETION;
protected IScriptProject scriptProject;
// Accpets completion proposals
protected CompletionRequestor requestor;
protected int startPosition;
protected int actualCompletionPosition;
protected int endPosition;
protected int offset;
protected char[] fileName = null;
protected boolean noProposal = true;
protected CategorizedProblem problem = null;
protected char[] source;
public ScriptCompletionEngine(/*
* ISearchableEnvironment nameEnvironment,
* CompletionRequestor requestor, Map
* settings, IScriptProject scriptProject
*/) {
super(null);
// this.scriptProject = scriptProject;
// this.requestor = requestor;
// this.nameEnvironment = nameEnvironment;
// this.lookupEnvironment = new LookupEnvironment(this,
// nameEnvironment);
}
protected CompletionProposal createProposal(int kind, int completionOffset) {
CompletionProposal proposal = CompletionProposal.create(kind,
completionOffset - this.offset);
return proposal;
}
// print
protected void printDebug(CategorizedProblem error) {
if (ScriptCompletionEngine.DEBUG) {
System.out.print("COMPLETION - completionFailure("); //$NON-NLS-1$
System.out.print(error);
System.out.println(")"); //$NON-NLS-1$
}
}
protected void printDebug(CompletionProposal proposal) {
StringBuffer buffer = new StringBuffer();
buffer.append("COMPLETION - "); //$NON-NLS-1$
switch (proposal.getKind()) {
case CompletionProposal.FIELD_REF:
buffer.append("FIELD_REF"); //$NON-NLS-1$
break;
case CompletionProposal.KEYWORD:
buffer.append("KEYWORD"); //$NON-NLS-1$
break;
case CompletionProposal.LABEL_REF:
buffer.append("LABEL_REF"); //$NON-NLS-1$
break;
case CompletionProposal.LOCAL_VARIABLE_REF:
buffer.append("LOCAL_VARIABLE_REF"); //$NON-NLS-1$
break;
case CompletionProposal.METHOD_DECLARATION:
buffer.append("METHOD_DECLARATION"); //$NON-NLS-1$
break;
case CompletionProposal.METHOD_REF:
buffer.append("METHOD_REF"); //$NON-NLS-1$
break;
case CompletionProposal.PACKAGE_REF:
buffer.append("PACKAGE_REF"); //$NON-NLS-1$
break;
case CompletionProposal.TYPE_REF:
buffer.append("TYPE_REF"); //$NON-NLS-1$
break;
case CompletionProposal.VARIABLE_DECLARATION:
buffer.append("VARIABLE_DECLARATION"); //$NON-NLS-1$
break;
case CompletionProposal.POTENTIAL_METHOD_DECLARATION:
buffer.append("POTENTIAL_METHOD_DECLARATION"); //$NON-NLS-1$
break;
case CompletionProposal.METHOD_NAME_REFERENCE:
buffer.append("METHOD_NAME_REFERENCE"); //$NON-NLS-1$
break;
default:
buffer.append("PROPOSAL"); //$NON-NLS-1$
break;
}
buffer.append("{\n");//$NON-NLS-1$
buffer
.append("\tCompletion[").append(proposal.getCompletion() == null ? "null".toCharArray() : proposal.getCompletion()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
buffer
.append("\tDeclarationKey[").append(proposal.getDeclarationKey() == null ? "null".toCharArray() : proposal.getDeclarationKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
buffer
.append("\tKey[").append(proposal.getKey() == null ? "null".toCharArray() : proposal.getKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
buffer
.append("\tName[").append(proposal.getName() == null ? "null".toCharArray() : proposal.getName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
buffer
.append("\tCompletionLocation[").append(proposal.getCompletionLocation()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
int start = proposal.getReplaceStart();
int end = proposal.getReplaceEnd();
buffer.append("\tReplaceStart[").append(start).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append("-ReplaceEnd[").append(end).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
if (this.source != null)
buffer
.append("\tReplacedText[").append(this.source, start, end - start).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
buffer
.append("\tTokenStart[").append(proposal.getTokenStart()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
buffer
.append("-TokenEnd[").append(proposal.getTokenEnd()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
buffer
.append("\tRelevance[").append(proposal.getRelevance()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append("}\n");//$NON-NLS-1$
System.out.println(buffer.toString());
}
// Source range
protected void setSourceRange(int start, int end) {
this.setSourceRange(start, end, true);
}
protected void setSourceRange(int start, int end,
boolean emptyTokenAdjstment) {
this.startPosition = start;
if (emptyTokenAdjstment) {
int endOfEmptyToken = getEndOfEmptyToken();
this.endPosition = endOfEmptyToken > end ? endOfEmptyToken : end;
} else {
this.endPosition = end;
}
}
protected abstract int getEndOfEmptyToken();
protected abstract String processMethodName(IMethod method, String token);
protected abstract String processTypeName(IType method, String token);
protected abstract String processFieldName(IField field, String token);
// what about onDemand types? Ignore them since it does not happen!
// import p1.p2.A.*;
public void findKeywords(char[] keyword, char[][] choices,
boolean canCompleteEmptyToken) {
if (choices == null || choices.length == 0)
return;
int length = keyword.length;
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < choices.length; i++) {
if (length <= choices[i].length
&& CharOperation.prefixEquals(keyword, choices[i],
false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(keyword,
choices[i]);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
/*
* access restriction for keywors
*/
// if (CharOperation.equals(choices[i], Keywords.TRUE)
// || CharOperation.equals(choices[i], Keywords.FALSE)) {
// relevance +=
// computeRelevanceForExpectingType(TypeBinding.BOOLEAN);
// relevance += computeRelevanceForQualification(false);
// }
this.noProposal = false;
if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
CompletionProposal proposal = this.createProposal(
CompletionProposal.KEYWORD,
this.actualCompletionPosition);
proposal.setName(choices[i]);
proposal.setCompletion(choices[i]);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
protected void findLocalVariables(char[] token, char[][] choices,
boolean canCompleteEmptyToken, boolean provideDollar) {
int kind = CompletionProposal.LOCAL_VARIABLE_REF;
findElements(token, choices, canCompleteEmptyToken, provideDollar, kind, Collections.EMPTY_MAP,Collections.EMPTY_MAP);
}
protected void findElements(char[] token, char[][] choices,
boolean canCompleteEmptyToken, boolean provideDollar, int kind, Map parameterNames, Map proposalInfos) {
if (choices == null || choices.length == 0)
return;
int length = token.length;
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < choices.length; i++) {
char[] co = choices[i];
if (!provideDollar && co.length > 1 && co[0] == '$') {
char co2[] = new char[co.length - 1];
System.arraycopy(co, 1, co2, 0, co2.length);
co = co2;
}
if (length <= choices[i].length
&& CharOperation.prefixEquals(token, co, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, co);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor.isIgnored(kind)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
kind,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
proposal.setName(co);
proposal.setCompletion(co);
proposal.setParameterNames((char[][]) parameterNames.get(choices[i]));
Object proposalInfo = proposalInfos.get(choices[i]);
if (proposalInfo != null)
{
proposal.extraInfo = proposalInfo;
}
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
public void findMethods(char[] token, boolean canCompleteEmptyToken,
List methods, List methodNames) {
if (methods == null || methods.size() == 0)
return;
int length = token.length;
String tok = new String(token);
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < methods.size(); i++) {
IMethod method = (IMethod) methods.get(i);
String qname = (String) methodNames.get(i);//processMethodName(method, tok);
char[] name = qname.toCharArray();
if (DLTKCore.DEBUG_COMPLETION) {
System.out.println("Completion:" + qname);
}
if (length <= name.length
&& CharOperation.prefixEquals(token, name, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, name);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor
.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
CompletionProposal.METHOD_DECLARATION,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
proposal.setModelElement(method);
String[] arguments = null;
try {
arguments = method.getParameters();
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
if (arguments != null && arguments.length > 0) {
char[][] args = new char[arguments.length][];
for (int j = 0; j < arguments.length; ++j) {
args[j] = arguments[j].toCharArray();
}
proposal.setParameterNames(args);
}
proposal.setName(name);
proposal.setCompletion(name);
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
protected void findLocalMethods(char[] token,
boolean canCompleteEmptyToken, List methods, List methodNames) {
if (methods == null || methods.size() == 0)
return;
int length = token.length;
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < methods.size(); i++) {
MethodDeclaration method = (MethodDeclaration) methods.get(i);
char[] name = ((String) (methodNames.get(i))).toCharArray();
if (length <= name.length
&& CharOperation.prefixEquals(token, name, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, name);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor
.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
CompletionProposal.METHOD_DECLARATION,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
List arguments = method.getArguments();
if (arguments != null && arguments.size() > 0) {
char[][] args = new char[arguments.size()][];
for (int j = 0; j < arguments.size(); ++j) {
args[j] = ((Argument) arguments.get(j))
.getName().toCharArray();
}
proposal.setParameterNames(args);
}
proposal.setName(name);
proposal.setCompletion(name);
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
protected void findMethods(char[] token, boolean canCompleteEmptyToken,
List methods) {
findMethods(token, canCompleteEmptyToken, methods,
CompletionProposal.METHOD_DECLARATION);
}
public void findFields(char[] token, boolean canCompleteEmptyToken,
List methods, String prefix) {
findFields(token, canCompleteEmptyToken, methods,
CompletionProposal.FIELD_REF, prefix);
}
protected void findMethods(char[] token, boolean canCompleteEmptyToken,
List methods, int kind) {
if (methods == null || methods.size() == 0)
return;
int length = token.length;
String tok = new String(token);
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < methods.size(); i++) {
IMethod method = (IMethod) methods.get(i);
String qname = processMethodName(method, tok);
char[] name = qname.toCharArray();
if (DLTKCore.DEBUG_COMPLETION) {
System.out.println("Completion:" + qname);
}
if (length <= name.length
&& CharOperation.prefixEquals(token, name, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, name);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor.isIgnored(kind)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
kind,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
proposal.setModelElement(method);
String[] arguments = null;
try {
arguments = method.getParameters();
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
if (arguments != null && arguments.length > 0) {
char[][] args = new char[arguments.length][];
for (int j = 0; j < arguments.length; ++j) {
args[j] = arguments[j].toCharArray();
}
proposal.setParameterNames(args);
}
proposal.setName(name);
proposal.setCompletion(name);
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
protected void findFields(char[] token, boolean canCompleteEmptyToken,
List fields, int kind, String prefix) {
if (fields == null || fields.size() == 0)
return;
int length = token.length;
String tok = new String(token);
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < fields.size(); i++) {
IField field = (IField) fields.get(i);
String qname = processFieldName(field, tok);
char[] name = qname.toCharArray();
if (DLTKCore.DEBUG_COMPLETION) {
System.out.println("Completion:" + qname);
}
if (length <= name.length
&& CharOperation.prefixEquals(token, name, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, name);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor.isIgnored(kind)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
kind,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
proposal.setModelElement(field);
proposal.setName(name);
proposal.setCompletion((prefix + qname).toCharArray());
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
public void findTypes(char[] token, boolean canCompleteEmptyToken,
List types) {
if (types == null || types.size() == 0)
return;
int length = token.length;
String tok = new String(token);
if (canCompleteEmptyToken || length > 0) {
for (int i = 0; i < types.size(); i++) {
IType type = (IType) types.get(i);
String qname = processTypeName(type, tok);
char[] name = qname.toCharArray();
if (DLTKCore.DEBUG_COMPLETION) {
System.out.println("Completion:" + qname);
}
if (length <= name.length
&& CharOperation.prefixEquals(token, name, false)) {
int relevance = computeBaseRelevance();
relevance += computeRelevanceForInterestingProposal();
relevance += computeRelevanceForCaseMatching(token, name);
relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no
// accept result
ScriptCompletionEngine.this.noProposal = false;
if (!ScriptCompletionEngine.this.requestor
.isIgnored(CompletionProposal.TYPE_REF)) {
CompletionProposal proposal = ScriptCompletionEngine.this
.createProposal(
CompletionProposal.TYPE_REF,
ScriptCompletionEngine.this.actualCompletionPosition);
// proposal.setSignature(getSignature(typeBinding));
// proposal.setPackageName(q);
// proposal.setTypeName(displayName);
proposal.setModelElement(type);
proposal.setName(name);
proposal.setCompletion(name);
// proposal.setFlags(Flags.AccDefault);
proposal.setReplaceRange(this.startPosition
- this.offset, this.endPosition - this.offset);
proposal.setRelevance(relevance);
this.requestor.accept(proposal);
if (DEBUG) {
this.printDebug(proposal);
}
}
}
}
}
}
// Relevance
private int computeBaseRelevance() {
return RelevanceConstants.R_DEFAULT;
}
private int computeRelevanceForInterestingProposal() {
return RelevanceConstants.R_INTERESTING;
}
protected int computeRelevanceForCaseMatching(char[] token,
char[] proposalName) {
if (this.options.camelCaseMatch) {
if (CharOperation.equals(token, proposalName, true)) {
return RelevanceConstants.R_CASE
+ RelevanceConstants.R_EXACT_NAME;
} else if (CharOperation.prefixEquals(token, proposalName, true)) {
return RelevanceConstants.R_CASE;
} else if (CharOperation.camelCaseMatch(token, proposalName)) {
return RelevanceConstants.R_CAMEL_CASE;
} else if (CharOperation.equals(token, proposalName, false)) {
return RelevanceConstants.R_EXACT_NAME;
}
} else if (CharOperation.prefixEquals(token, proposalName, true)) {
if (CharOperation.equals(token, proposalName, true)) {
return RelevanceConstants.R_CASE
+ RelevanceConstants.R_EXACT_NAME;
} else {
return RelevanceConstants.R_CASE;
}
} else if (CharOperation.equals(token, proposalName, false)) {
return RelevanceConstants.R_EXACT_NAME;
}
return 0;
}
protected int computeRelevanceForRestrictions(int accessRuleKind) {
if (accessRuleKind == IAccessRule.K_ACCESSIBLE) {
return RelevanceConstants.R_NON_RESTRICTED;
}
return 0;
}
public void setEnvironment(ISearchableEnvironment environment) {
this.nameEnvironment = environment;
this.lookupEnvironment = new LookupEnvironment(this, nameEnvironment);
}
public void setOptions(Map options) {
}
public void setProject(IScriptProject project) {
this.scriptProject = project;
}
public void setRequestor(CompletionRequestor requestor) {
this.requestor = requestor;
}
}