/******************************************************************************* * Copyright (c) 2014 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 * Zend Technologies * Dawid PakuĊ‚a - Allow change context and strategies from UI *******************************************************************************/ package org.eclipse.php.internal.core.codeassist; import java.util.*; import org.eclipse.core.runtime.IPath; import org.eclipse.dltk.codeassist.ScriptCompletionEngine; import org.eclipse.dltk.compiler.env.IModuleSource; import org.eclipse.dltk.core.*; import org.eclipse.dltk.internal.core.ModelManager; import org.eclipse.php.core.codeassist.*; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.internal.core.PHPCorePlugin; import org.eclipse.php.internal.core.codeassist.contexts.CompletionContextResolver; import org.eclipse.php.internal.core.codeassist.strategies.CompletionStrategyFactory; import org.eclipse.php.internal.core.typeinference.PHPModelUtils; /** * Completion engine for PHP. This engine uses structured document for defining * the completion context; AST is not used since it lacks error recovery for all * cases. * * @author michael */ public class PHPCompletionEngine extends ScriptCompletionEngine implements ICompletionReporter { private int relevanceKeyword; private int relevanceMethod; private int relevanceClass; private int relevanceVar; private int relevanceConst; private Map<? super Object, Object> processedElements = new HashMap<Object, Object>(); private Set<? super Object> processedPaths = new HashSet<Object>(); private Set<IField> processedFields = new TreeSet<IField>(new Comparator<IField>() { public int compare(IField f1, IField f2) { // filter duplications of variables if (PHPModelUtils.isSameField((IField) f1, (IField) f2)) { return 0; } return f1.getElementName().compareTo(f2.getElementName()); } }); IModuleSource module; public void complete(IModuleSource module, int position, int i) { complete(module, position, i, false); } public void complete(IModuleSource module, int position, int i, boolean waitForBuilder) { if (!PHPCorePlugin.toolkitInitialized) { return; } if (requestor instanceof IPHPCompletionRequestor) { ((IPHPCompletionRequestor) requestor).setOffset(offset); } if (waitForBuilder) { ModelManager.getModelManager().getIndexManager().waitUntilReady(); } this.module = module; relevanceKeyword = RELEVANCE_KEYWORD; relevanceMethod = RELEVANCE_METHOD; relevanceClass = RELEVANCE_CLASS; relevanceVar = RELEVANCE_VAR; relevanceConst = RELEVANCE_CONST; try { ICompletionContextResolver[] contextResolvers; ICompletionStrategyFactory[] strategyFactories; if (requestor instanceof IPHPCompletionRequestorExtension) { contextResolvers = ((IPHPCompletionRequestorExtension) requestor).getContextResolvers(); strategyFactories = ((IPHPCompletionRequestorExtension) requestor).getStrategyFactories(); } else { contextResolvers = CompletionContextResolver.getActive(); strategyFactories = CompletionStrategyFactory.getActive(); } CompletionCompanion companion = new CompletionCompanion(); org.eclipse.dltk.core.ISourceModule sourceModule = (org.eclipse.dltk.core.ISourceModule) module .getModelElement(); for (ICompletionContextResolver resolver : contextResolvers) { ICompletionContext[] contexts = resolver.resolve(sourceModule, position, requestor, companion); if (contexts != null && contexts.length > 0) { for (ICompletionStrategyFactory factory : strategyFactories) { ICompletionStrategy[] strategies = factory.create(contexts); if (strategies != null && strategies.length > 0) { for (ICompletionStrategy strategy : strategies) { strategy.init(companion); try { strategy.apply(this); } catch (Exception e) { PHPCorePlugin.log(e); } } } } } } } finally { processedElements.clear(); processedPaths.clear(); } } @Override public void reportField(IField field, String suffix, ISourceRange replaceRange, boolean removeDollar) { reportField(field, suffix, replaceRange, removeDollar, 0); } @Override public void reportField(IField field, String suffix, ISourceRange replaceRange, boolean removeDollar, int subRelevance) { reportField(field, suffix, replaceRange, removeDollar, subRelevance, null); } @Override public void reportField(IField field, String suffix, ISourceRange replaceRange, boolean removeDollar, int subRelevance, Object extraInfo) { if (processedFields.contains(field)) { return; } processedFields.add(field); int flags = 0; try { flags = field.getFlags(); } catch (ModelException e) { PHPCorePlugin.log(e); } int relevance = PHPFlags.isConstant(flags) ? relevanceConst : relevanceVar; relevance += subRelevance; noProposal = false; if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) { CompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, actualCompletionPosition); proposal.setName(field.getElementName()); String completion = field.getElementName() + suffix; if (removeDollar && completion.startsWith("$")) { //$NON-NLS-1$ completion = completion.substring(1); } proposal.setCompletion(completion); proposal.setExtraInfo(extraInfo); proposal.setModelElement(field); proposal.setFlags(flags); proposal.setRelevance(relevance); proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } } public void reportField(IField field, String completion, ISourceRange replaceRange, int subRelevance) { if (processedFields.contains(field)) { return; } processedFields.add(field); int flags = 0; try { flags = field.getFlags(); } catch (ModelException e) { PHPCorePlugin.log(e); } int relevance = PHPFlags.isConstant(flags) ? relevanceConst : relevanceVar; relevance += subRelevance; noProposal = false; if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) { CompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, actualCompletionPosition); proposal.setName(field.getElementName()); proposal.setCompletion(completion); proposal.setModelElement(field); proposal.setFlags(flags); proposal.setRelevance(relevance); proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } } @Override public void reportKeyword(String keyword, String suffix, ISourceRange replaceRange) { reportKeyword(keyword, suffix, replaceRange, 0); } public void reportKeyword(String keyword, String suffix, ISourceRange replaceRange, int subRelevance) { if (processedElements.containsKey(keyword)) { return; } processedElements.put(keyword, keyword); noProposal = false; if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) { CompletionProposal proposal = createProposal(CompletionProposal.KEYWORD, actualCompletionPosition); proposal.setName(keyword); proposal.setCompletion(keyword + suffix); proposal.setRelevance(relevanceKeyword + subRelevance); proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } } @Override public void reportMethod(IMethod method, String suffix, ISourceRange replaceRange, Object extraInfo) { reportMethod(method, suffix, replaceRange, extraInfo, 0); } public void reportMethod(IMethod method, String suffix, ISourceRange replaceRange, Object extraInfo, int subRelevance) { if (processedElements.containsKey(method) && ((IMethod) processedElements.get(method)).getParent().getClass() == method.getParent().getClass()) { return; } processedElements.put(method, method); noProposal = false; if (!requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) { CompletionProposal proposal = createProposal(CompletionProposal.METHOD_DECLARATION, actualCompletionPosition); proposal.setExtraInfo(extraInfo); // show method parameter names: String[] params = null; try { params = method.getParameterNames(); } catch (ModelException e) { PHPCorePlugin.log(e); } if (params != null && params.length > 0) { proposal.setParameterNames(params); } String elementName = method.getElementName(); String completionName = elementName; proposal.setModelElement(method); proposal.setName(elementName); int relevance = relevanceMethod + subRelevance; proposal.setCompletion((completionName + suffix)); try { proposal.setIsConstructor(elementName.equals("__construct") //$NON-NLS-1$ || method.isConstructor()); proposal.setFlags(method.getFlags()); } catch (ModelException e) { if (DEBUG) { e.printStackTrace(); } } proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); proposal.setRelevance(relevance); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } } @Override public void reportMethod(IMethod method, String suffix, ISourceRange replaceRange) { reportMethod(method, suffix, replaceRange, null); } @Override public void reportType(IType type, String suffix, ISourceRange replaceRange) { reportType(type, suffix, replaceRange, null); } @Override public void reportType(IType type, String suffix, ISourceRange replaceRange, Object extraInfo) { reportType(type, suffix, replaceRange, extraInfo, 0); } @Override public void reportType(IType type, String suffix, ISourceRange replaceRange, Object extraInfo, int subRelevance) { if (processedElements.containsKey(type) && processedElements.get(type).getClass() == type.getClass()) { return; } processedElements.put(type, type); noProposal = false; if (!requestor.isIgnored(CompletionProposal.TYPE_REF)) { CompletionProposal proposal = createProposal(CompletionProposal.TYPE_REF, actualCompletionPosition); proposal.setExtraInfo(extraInfo); // Support parameter names for constructor: if (requestor.isContextInformationMode()) { try { for (IMethod method : type.getMethods()) { if (method.isConstructor()) { String[] params = method.getParameterNames(); if (params != null && params.length > 0) { proposal.setParameterNames(params); } break; } } } catch (ModelException e) { PHPCorePlugin.log(e); } } String elementName = type.getElementName(); String completionName = elementName; proposal.setModelElement(type); proposal.setName(elementName); int relevance = relevanceClass + subRelevance; proposal.setCompletion(completionName + suffix); try { proposal.setFlags(type.getFlags()); } catch (ModelException e) { PHPCorePlugin.log(e); } proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); proposal.setRelevance(relevance); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } } @Override public void reportResource(IModelElement model, IPath relative, String suffix, ISourceRange replaceRange) { if (processedElements.containsKey(model) || processedPaths.contains(relative)) { return; } processedElements.put(model, model); processedPaths.add(relative); noProposal = false; CompletionProposal proposal = null; if (model.getElementType() == IModelElement.SCRIPT_FOLDER && !requestor.isIgnored(CompletionProposal.PACKAGE_REF)) { proposal = createProposal(CompletionProposal.PACKAGE_REF, actualCompletionPosition); } else if (model.getElementType() == IModelElement.PROJECT_FRAGMENT) { proposal = createProposal(CompletionProposal.PACKAGE_REF, actualCompletionPosition); } else if (!requestor.isIgnored(CompletionProposal.KEYWORD)) { proposal = createProposal(CompletionProposal.KEYWORD, actualCompletionPosition); } if (proposal == null) { return; } proposal.setName(relative.toString()); proposal.setCompletion((relative.toString() + suffix)); proposal.setRelevance(relevanceKeyword); proposal.setReplaceRange(replaceRange.getOffset(), replaceRange.getOffset() + replaceRange.getLength()); proposal.setModelElement(model); this.requestor.accept(proposal); if (DEBUG) { this.printDebug(proposal); } } protected int getEndOfEmptyToken() { return 0; } protected String processMethodName(IMethod method, String token) { return method.getElementName(); } protected String processTypeName(IType type, String token) { return type.getElementName(); } public IModuleSource getModule() { return module; } }