blob: 5e17054f52865584b64a3ec6d1acc3fc79f2d6b0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.php.editor.completion;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.api.editor.completion.Completion;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.api.lexer.TokenUtilities;
import org.netbeans.modules.csl.api.CompletionProposal;
import org.netbeans.modules.csl.api.ElementHandle;
import org.netbeans.modules.csl.api.ElementKind;
import org.netbeans.modules.csl.api.HtmlFormatter;
import org.netbeans.modules.csl.api.Modifier;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.php.api.PhpVersion;
import org.netbeans.modules.php.api.util.StringUtils;
import org.netbeans.modules.php.editor.Cache;
import org.netbeans.modules.php.editor.CodeUtils;
import org.netbeans.modules.php.editor.completion.CompletionContextFinder.CompletionContext;
import org.netbeans.modules.php.editor.completion.CompletionContextFinder.KeywordCompletionType;
import org.netbeans.modules.php.editor.actions.IconsUtils;
import org.netbeans.modules.php.editor.api.ElementQuery;
import org.netbeans.modules.php.editor.api.PhpElementKind;
import org.netbeans.modules.php.editor.api.QualifiedName;
import org.netbeans.modules.php.editor.api.QualifiedNameKind;
import org.netbeans.modules.php.editor.api.elements.AliasedElement;
import org.netbeans.modules.php.editor.api.elements.BaseFunctionElement;
import org.netbeans.modules.php.editor.api.elements.BaseFunctionElement.PrintAs;
import org.netbeans.modules.php.editor.api.elements.ClassElement;
import org.netbeans.modules.php.editor.api.elements.ConstantElement;
import org.netbeans.modules.php.editor.api.elements.FieldElement;
import org.netbeans.modules.php.editor.api.elements.FullyQualifiedElement;
import org.netbeans.modules.php.editor.api.elements.FunctionElement;
import org.netbeans.modules.php.editor.api.elements.InterfaceElement;
import org.netbeans.modules.php.editor.api.elements.MethodElement;
import org.netbeans.modules.php.editor.api.elements.NamespaceElement;
import org.netbeans.modules.php.editor.api.elements.ParameterElement;
import org.netbeans.modules.php.editor.api.elements.ParameterElement.OutputType;
import org.netbeans.modules.php.editor.api.elements.PhpElement;
import org.netbeans.modules.php.editor.api.elements.TraitElement;
import org.netbeans.modules.php.editor.api.elements.TypeConstantElement;
import org.netbeans.modules.php.editor.api.elements.TypeElement;
import org.netbeans.modules.php.editor.api.elements.TypeMemberElement;
import org.netbeans.modules.php.editor.api.elements.TypeNameResolver;
import org.netbeans.modules.php.editor.api.elements.TypeResolver;
import org.netbeans.modules.php.editor.api.elements.VariableElement;
import org.netbeans.modules.php.editor.codegen.CodegenUtils;
import org.netbeans.modules.php.editor.elements.ParameterElementImpl;
import org.netbeans.modules.php.editor.elements.TypeNameResolverImpl;
import org.netbeans.modules.php.editor.indent.CodeStyle;
import org.netbeans.modules.php.editor.index.PredefinedSymbolElement;
import org.netbeans.modules.php.editor.lexer.PHPTokenId;
import org.netbeans.modules.php.editor.model.FileScope;
import org.netbeans.modules.php.editor.model.Model;
import org.netbeans.modules.php.editor.model.ModelUtils;
import org.netbeans.modules.php.editor.model.NamespaceScope;
import org.netbeans.modules.php.editor.model.VariableName;
import org.netbeans.modules.php.editor.model.VariableScope;
import org.netbeans.modules.php.editor.model.impl.Type;
import org.netbeans.modules.php.editor.model.impl.VariousUtils;
import org.netbeans.modules.php.editor.model.nodes.NamespaceDeclarationInfo;
import org.netbeans.modules.php.editor.NavUtils;
import org.netbeans.modules.php.editor.options.CodeCompletionPanel.CodeCompletionType;
import org.netbeans.modules.php.editor.options.OptionsUtils;
import org.netbeans.modules.php.editor.parser.PHPParseResult;
import org.netbeans.modules.php.editor.parser.astnodes.ASTNode;
import org.netbeans.modules.php.editor.parser.astnodes.BodyDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.Program;
import org.netbeans.modules.php.project.api.PhpLanguageProperties;
import org.openide.filesystems.FileObject;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListeners;
/**
*
* @author Tomasz.Slota@Sun.COM
*/
public abstract class PHPCompletionItem implements CompletionProposal {
@StaticResource
private static final String PHP_KEYWORD_ICON = "org/netbeans/modules/php/editor/resources/php16Key.png"; //NOI18N
protected static final ImageIcon KEYWORD_ICON = new ImageIcon(ImageUtilities.loadImage(PHP_KEYWORD_ICON));
final CompletionRequest request;
private final ElementHandle element;
private QualifiedNameKind generateAs;
private static ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
private static final Cache<FileObject, PhpLanguageProperties> PROPERTIES_CACHE
= new Cache<>(new WeakHashMap<FileObject, PhpLanguageProperties>());
private final boolean isPlatform;
private final boolean isDeprecated;
private PhpVersion phpVersion;
PHPCompletionItem(ElementHandle element, CompletionRequest request, QualifiedNameKind generateAs) {
this.request = request;
this.element = element;
this.generateAs = generateAs;
if (element instanceof PhpElement) {
final PhpElement phpElement = (PhpElement) element;
isPlatform = phpElement.isPlatform();
isDeprecated = phpElement.isDeprecated();
} else {
isPlatform = false;
isDeprecated = false;
}
}
PHPCompletionItem(ElementHandle element, CompletionRequest request) {
this(element, request, null);
}
@Override
public int getAnchorOffset() {
return request.anchor;
}
@Override
public ElementHandle getElement() {
return element;
}
@Override
public String getName() {
return element.getName();
}
@Override
public String getSortText() {
return getName();
}
@Override
public int getSortPrioOverride() {
return 0;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.name(getKind(), true);
String name = getName();
if (CodeUtils.isSyntheticTypeName(name)) {
// anonymous class
name = "{}"; // NOI18N
}
if (isDeprecated()) {
formatter.deprecated(true);
formatter.appendText(name);
formatter.deprecated(false);
} else {
formatter.appendText(name);
}
formatter.name(getKind(), false);
return formatter.getText();
}
@Override
public ImageIcon getIcon() {
return null;
}
@Override
public Set<Modifier> getModifiers() {
Set<Modifier> emptyModifiers = Collections.emptySet();
ElementHandle handle = getElement();
return (handle != null) ? handle.getModifiers() : emptyModifiers;
}
public String getFileNameURL() {
ElementHandle elem = getElement();
return (elem instanceof PhpElement) ? ((PhpElement) elem).getFilenameUrl() : ""; //NOI18N
}
@Override
public boolean isSmart() {
return element instanceof AliasedElement
|| (request.currentlyEditedFileURL != null && request.currentlyEditedFileURL.equals(getFileNameURL()));
}
protected boolean isDeprecated() {
return isDeprecated;
}
boolean isPlatform() {
return isPlatform;
}
// for unit tests for custom template
void setPhpVersion(PhpVersion phpVersion) {
this.phpVersion = phpVersion;
}
protected PhpVersion getPhpVersion(@NullAllowed FileObject file) {
if (phpVersion != null) {
return phpVersion;
}
return file != null ? CodeUtils.getPhpVersion(file) : PhpVersion.getDefault();
}
private static NamespaceDeclaration findEnclosingNamespace(PHPParseResult info, int offset) {
final Program program = info.getProgram();
List<ASTNode> nodes = NavUtils.underCaret(info, Math.min((program != null) ? program.getEndOffset() : offset, offset));
for (ASTNode node : nodes) {
if (node instanceof NamespaceDeclaration) {
return (NamespaceDeclaration) node;
}
}
return null;
}
@Override
public String getCustomInsertTemplate() {
return null;
}
@Override
public String getInsertPrefix() {
StringBuilder template = new StringBuilder();
ElementHandle elem = getElement();
if (elem instanceof MethodElement) {
final MethodElement method = (MethodElement) elem;
if (method.isConstructor() && request.context.equals(CompletionContext.NEW_CLASS)) {
elem = method.getType();
}
}
if (elem instanceof FullyQualifiedElement) {
FullyQualifiedElement ifq = (FullyQualifiedElement) elem;
final QualifiedName qn = QualifiedName.create(request.prefix);
final FileObject fileObject = request.result.getSnapshot().getSource().getFileObject();
PhpLanguageProperties props = PROPERTIES_CACHE.get(fileObject);
if (props == null) {
props = PhpLanguageProperties.forFileObject(fileObject);
PropertyChangeListener propertyChangeListener = WeakListeners.propertyChange(new PhpVersionChangeListener(fileObject), props);
props.addPropertyChangeListener(propertyChangeListener);
PROPERTIES_CACHE.save(fileObject, props);
}
if (props.getPhpVersion() != PhpVersion.PHP_5) {
if (generateAs == null) {
CodeCompletionType codeCompletionType = OptionsUtils.codeCompletionType();
switch (codeCompletionType) {
case FULLY_QUALIFIED:
template.append(ifq.getFullyQualifiedName());
return template.toString();
case UNQUALIFIED:
template.append(getName());
return template.toString();
case SMART:
generateAs = qn.getKind();
break;
default:
assert false : codeCompletionType;
}
}
} else {
template.append(getName());
return template.toString();
}
switch (generateAs) {
case FULLYQUALIFIED:
template.append(ifq.getFullyQualifiedName());
break;
case QUALIFIED:
final String fqn = ifq.getFullyQualifiedName().toString();
int indexOf = fqn.toLowerCase().indexOf(qn.toNamespaceName().toString().toLowerCase());
if (indexOf != -1) {
template.append(fqn.substring(indexOf == 0 ? 1 : indexOf));
break;
}
case UNQUALIFIED:
String enclosingScopeName = ifq.getIn();
boolean fncOrConstFromDefaultNamespace = (((ifq instanceof FunctionElement) || (ifq instanceof ConstantElement))
&& (enclosingScopeName == null || enclosingScopeName.isEmpty())
&& NamespaceDeclarationInfo.DEFAULT_NAMESPACE_NAME.equals(ifq.getNamespaceName().toString()));
final boolean isUnqualified = ifq.isAliased()
&& (ifq instanceof AliasedElement) && ((AliasedElement) ifq).isNameAliased();
if (!fncOrConstFromDefaultNamespace && !isUnqualified) {
Model model = request.result.getModel();
NamespaceDeclaration namespaceDeclaration = findEnclosingNamespace(request.result, request.anchor);
NamespaceScope namespaceScope = ModelUtils.getNamespaceScope(namespaceDeclaration, model.getFileScope());
if (namespaceScope != null) {
LinkedList<String> segments = ifq.getFullyQualifiedName().getSegments();
QualifiedName fqna = QualifiedName.create(false, segments);
if (!namespaceScope.isDefaultNamespace() || !fqna.getKind().isUnqualified()) {
QualifiedName suffix = VariousUtils.getPreferredName(fqna, namespaceScope);
if (suffix != null) {
template.append(suffix.toString());
break;
}
}
}
}
template.append(getName());
break;
default:
assert false : generateAs;
}
// XXX improve?
String tpl = template.toString();
String extraPrefix = request.extraPrefix;
if (StringUtils.hasText(extraPrefix)) {
if (tpl.startsWith(extraPrefix)) {
tpl = tpl.substring(extraPrefix.length());
} else {
assert false : "[" + tpl + "] should start with [" + extraPrefix + "]";
}
}
return tpl;
}
return getName();
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
if (element instanceof TypeMemberElement) {
TypeMemberElement classMember = (TypeMemberElement) element;
TypeElement type = classMember.getType();
String name = type.getName();
if (CodeUtils.isSyntheticTypeName(name)) {
// anonymous class
formatter.appendText("{}"); // NOI18N
return formatter.getText();
}
QualifiedName qualifiedName = type.getNamespaceName();
if (qualifiedName.isDefaultNamespace()) {
formatter.appendText(name);
return formatter.getText();
} else {
formatter.appendText(type.getFullyQualifiedName().toString());
return formatter.getText();
}
}
final String in = element.getIn();
if (in != null && in.length() > 0) {
formatter.appendText(in);
return formatter.getText();
} else if (element instanceof PhpElement) {
PhpElement ie = (PhpElement) element;
if (isPlatform) {
return NbBundle.getMessage(PHPCompletionItem.class, "PHPPlatform");
}
String filename = ie.getFilenameUrl();
int index = filename.lastIndexOf('/');
if (index != -1) {
filename = filename.substring(index + 1);
}
formatter.appendText(filename);
return formatter.getText();
}
return null;
}
public static boolean insertOnlyMethodsName(CompletionRequest request) {
if (request.insertOnlyMethodsName != null) {
return request.insertOnlyMethodsName;
}
boolean result = false;
TokenHierarchy<?> tokenHierarchy = request.result.getSnapshot().getTokenHierarchy();
TokenSequence<PHPTokenId> tokenSequence = (TokenSequence<PHPTokenId>) tokenHierarchy.tokenSequence();
if (tokenSequence != null) {
VariableScope variableScope = request.result.getModel().getVariableScope(request.anchor);
if (variableScope != null) {
tokenSequence = tokenSequence.subSequence(request.anchor, variableScope.getBlockRange().getEnd());
}
boolean wasWhitespace = false;
while (tokenSequence.moveNext()) {
Token<PHPTokenId> token = tokenSequence.token();
PHPTokenId id = token.id();
if (PHPTokenId.PHP_STRING.equals(id)) {
if (wasWhitespace) {
// this needs brackets: curl_set^ curl_setopt($ch, $option, $ch);
break;
} else {
// this doesn't need brackets: curl_setopt^ ($ch, $option, $ch);
continue;
}
} else if (PHPTokenId.WHITESPACE.equals(id)) {
wasWhitespace = true;
continue;
} else if (PHPTokenId.PHP_TOKEN.equals(id) && TokenUtilities.textEquals(token.text(), "(")) { //NOI18N
result = true;
break;
} else {
break;
}
}
}
return result;
}
static class NewClassItem extends MethodElementItem {
/**
* @return more than one instance in case if optional parameters exists
*/
static List<NewClassItem> getNewClassItems(final MethodElement methodElement, CompletionRequest request) {
final List<NewClassItem> retval = new ArrayList<>();
List<FunctionElementItem> items = FunctionElementItem.getItems(methodElement, request);
for (FunctionElementItem functionElementItem : items) {
retval.add(new NewClassItem(functionElementItem));
}
return retval;
}
private NewClassItem(FunctionElementItem function) {
super(function);
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
ElementHandle element = getElement();
if (element != null && element.getIn() != null) {
String namespaceName = ((MethodElement) element).getType().getNamespaceName().toString();
if (namespaceName != null && !NamespaceDeclarationInfo.DEFAULT_NAMESPACE_NAME.equals(namespaceName)) {
formatter.appendText(namespaceName);
return formatter.getText();
}
}
return super.getRhsHtml(formatter);
}
@Override
public String getName() {
ElementHandle element = getElement();
String in = element == null ? null : element.getIn();
return (in != null) ? in : super.getName();
}
@Override
public ElementKind getKind() {
return ElementKind.CONSTRUCTOR;
}
@Override
public boolean isSmart() {
return (getElement() instanceof AliasedElement) ? true : super.isSmart();
}
}
public static class MethodElementItem extends FunctionElementItem {
private final boolean completeAccessPrefix;
/**
* @return more than one instance in case if optional parameters exists
*/
static List<MethodElementItem> getItems(final MethodElement methodElement, CompletionRequest request) {
return getItems(methodElement, request, false);
}
/**
* @return more than one instance in case if optional parameters exists
*/
static List<MethodElementItem> getItems(final MethodElement methodElement, CompletionRequest request, boolean completeAccessPrefix) {
final List<MethodElementItem> retval = new ArrayList<>();
List<FunctionElementItem> items = FunctionElementItem.getItems(methodElement, request);
for (FunctionElementItem functionElementItem : items) {
retval.add(new MethodElementItem(functionElementItem, completeAccessPrefix));
}
return retval;
}
MethodElementItem(FunctionElementItem function) {
this(function, false);
}
MethodElementItem(FunctionElementItem function, boolean completeAccessPrefix) {
super(function.getBaseFunctionElement(), function.request, function.parameters);
this.completeAccessPrefix = completeAccessPrefix;
}
@Override
public String getCustomInsertTemplate() {
String prefix = ""; // NOI18N
if (completeAccessPrefix) {
Set<Modifier> modifiers = getModifiers();
prefix = modifiers.contains(Modifier.STATIC) ? "self::" : "$this->"; // NOI18N
}
return prefix + super.getCustomInsertTemplate();
}
}
private static class ExistingVariableResolver {
private final CompletionRequest request;
private final int caretOffset;
private final List<VariableName> usedVariables = new LinkedList<>();
private static final RequestProcessor RP = new RequestProcessor("ExistingVariableResolver"); //NOI18N
private static final Logger LOGGER = Logger.getLogger(ExistingVariableResolver.class.getName());
private static final int RESOLVING_TIMEOUT = 300;
public ExistingVariableResolver(CompletionRequest request) {
this.request = request;
caretOffset = request.anchor;
}
public ParameterElement resolveVariable(final ParameterElement param) {
if (OptionsUtils.codeCompletionSmartParametersPreFilling()) {
Future<VariableName> futureVariableToUse = RP.submit(new Callable<VariableName>() {
@Override
public VariableName call() throws Exception {
Collection<? extends VariableName> declaredVariables = getDeclaredVariables();
VariableName variableToUse = null;
if (declaredVariables != null) {
int oldOffset = 0;
for (VariableName variable : declaredVariables) {
if (!usedVariables.contains(variable) && !variable.representsThis()) {
if (isPreviousVariable(variable)) {
if (hasCorrectType(variable, param.getTypes())) {
if (variable.getName().equals(param.getName())) {
variableToUse = variable;
break;
}
int newOffset = variable.getNameRange().getStart();
if (newOffset > oldOffset) {
oldOffset = newOffset;
variableToUse = variable;
}
}
}
}
}
}
return variableToUse;
}
});
VariableName variableToUseName = null;
try {
variableToUseName = futureVariableToUse.get(RESOLVING_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
LOGGER.log(Level.FINE, "Resolving of existing variables has been interrupted.");
} catch (ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Exception has been thrown during resolving of existing variables.", ex);
} catch (TimeoutException ex) {
LOGGER.log(Level.FINE, "Timeout for resolving existing variables has been exceed: {0}", RESOLVING_TIMEOUT);
}
if (variableToUseName != null) {
usedVariables.add(variableToUseName);
return new ParameterElementImpl(
variableToUseName.getName(),
param.getDefaultValue(),
param.getOffset(),
param.getTypes(),
param.isMandatory(),
param.hasDeclaredType(),
param.isReference(),
param.isVariadic());
}
}
return param;
}
private Collection<? extends VariableName> getDeclaredVariables() {
VariableScope variableScope = request.result.getModel().getVariableScope(caretOffset);
if (variableScope != null) {
return variableScope.getDeclaredVariables();
}
return null;
}
private boolean isPreviousVariable(VariableName variable) {
int offsetDiff = caretOffset - variable.getNameRange().getStart();
if (offsetDiff > 0) {
return true;
}
return false;
}
private boolean hasCorrectType(VariableName variable, Set<TypeResolver> possibleTypes) {
Collection<? extends String> typeNames = variable.getTypeNames(caretOffset);
if (!typeNames.isEmpty()) {
for (TypeResolver type : possibleTypes) {
if (typeNames.contains(type.getRawTypeName()) || Type.MIXED.equals(type.getRawTypeName())
|| (typeNames.contains(Type.REAL) && Type.FLOAT.equals(type.getRawTypeName()))
|| (typeNames.contains(Type.INT) && Type.INTEGER.equals(type.getRawTypeName()))) {
return true;
}
}
}
return false;
}
}
static class FunctionElementItem extends PHPCompletionItem {
private List<ParameterElement> parameters;
/**
* @return more than one instance in case if optional parameters exists
*/
static List<FunctionElementItem> getItems(final BaseFunctionElement function, CompletionRequest request) {
return getItems(function, request, null);
}
static List<FunctionElementItem> getItems(final BaseFunctionElement function, CompletionRequest request, QualifiedNameKind generateAs) {
final List<FunctionElementItem> retval = new ArrayList<>();
final List<ParameterElement> parameters = new ArrayList<>();
for (ParameterElement param : function.getParameters()) {
if (!param.isMandatory()) {
if (retval.isEmpty()) {
retval.add(new FunctionElementItem(function, request, parameters, generateAs));
}
parameters.add(param);
retval.add(new FunctionElementItem(function, request, parameters, generateAs));
} else {
//assert retval.isEmpty():param.asString();
parameters.add(param);
}
}
if (retval.isEmpty()) {
retval.add(new FunctionElementItem(function, request, parameters, generateAs));
}
return retval;
}
FunctionElementItem(BaseFunctionElement function, CompletionRequest request, List<ParameterElement> parameters) {
this(function, request, parameters, null);
}
FunctionElementItem(BaseFunctionElement function, CompletionRequest request, List<ParameterElement> parameters, QualifiedNameKind generateAs) {
super(function, request, generateAs);
this.parameters = new ArrayList<>(parameters);
}
public BaseFunctionElement getBaseFunctionElement() {
return (BaseFunctionElement) getElement();
}
@Override
public ElementKind getKind() {
return getBaseFunctionElement().getPhpElementKind().getElementKind();
}
@Override
public String getInsertPrefix() {
// used for filtering purposes
return getName();
}
@Override
public String getCustomInsertTemplate() {
StringBuilder template = new StringBuilder();
template.append(super.getInsertPrefix());
if (!insertOnlyMethodsName(request)) {
template.append("("); //NOI18N
List<String> params = getInsertParams();
for (int i = 0; i < params.size(); i++) {
String param = params.get(i);
if (param.startsWith("&")) { //NOI18N
param = param.substring(1);
}
template.append(String.format("${php-cc-%d default=\"%s\"}", i, param));
if (i < params.size() - 1) {
template.append(", "); //NOI18N
}
}
template.append(')');
}
return template.toString();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
ElementKind kind = getKind();
formatter.name(kind, true);
if (emphasisName()) {
formatter.emphasis(true);
if (isDeprecated()) {
formatter.deprecated(true);
formatter.appendText(getName());
formatter.deprecated(false);
} else {
formatter.appendText(getName());
}
formatter.emphasis(false);
} else {
if (isDeprecated()) {
formatter.deprecated(true);
formatter.appendText(getName());
formatter.deprecated(false);
} else {
formatter.appendText(getName());
}
}
formatter.name(kind, false);
formatter.appendHtml("("); // NOI18N
formatter.parameters(true);
appendParamsStr(formatter);
formatter.parameters(false);
formatter.appendHtml(")"); // NOI18N
return formatter.getText();
}
protected boolean emphasisName() {
return true;
}
public List<String> getInsertParams() {
List<String> insertParams = new LinkedList<>();
final ExistingVariableResolver existingVariableResolver = new ExistingVariableResolver(request);
for (ParameterElement parameter : parameters) {
insertParams.add(existingVariableResolver.resolveVariable(parameter).getName());
}
return insertParams;
}
@Override
public String getSortText() {
return getName() + parameters.size();
}
private void appendParamsStr(HtmlFormatter formatter) {
List<ParameterElement> allParameters = parameters;
for (int i = 0; i < allParameters.size(); i++) {
ParameterElement parameter = allParameters.get(i);
if (i != 0) {
formatter.appendText(", "); // NOI18N
}
final String paramTpl = parameter.asString(OutputType.SHORTEN_DECLARATION);
if (!parameter.isMandatory()) {
formatter.appendText(paramTpl);
} else {
formatter.emphasis(true);
formatter.appendText(paramTpl);
formatter.emphasis(false);
}
}
}
}
static class BasicFieldItem extends PHPCompletionItem {
private String typeName;
public static BasicFieldItem getItem(PhpElement field, String type, CompletionRequest request) {
return new BasicFieldItem(field, type, request);
}
private BasicFieldItem(PhpElement field, String typeName, CompletionRequest request) {
super(field, request);
this.typeName = typeName;
}
@Override
public String getInsertPrefix() {
Completion.get().showToolTip();
return getName();
}
@Override
public ElementKind getKind() {
//TODO: variable just because originally VARIABLE was returned and thus all tests fail
//return ElementKind.FIELD;
return ElementKind.VARIABLE;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.type(true);
formatter.appendText(getTypeName() == null ? "" : getTypeName()); //NOI18N
formatter.type(false);
formatter.appendText(" "); //NOI18N
formatter.name(getKind(), true);
if (isDeprecated()) {
formatter.deprecated(true);
}
formatter.appendText(getName());
if (isDeprecated()) {
formatter.deprecated(false);
}
formatter.name(getKind(), false);
return formatter.getText();
}
@Override
public String getName() {
ElementHandle element = getElement();
assert element != null;
final String name = element.getName();
return name.startsWith("$") ? name.substring(1) : name;
}
/**
* @return the typeName
*/
protected String getTypeName() {
return typeName;
}
}
static class FieldItem extends BasicFieldItem {
private final boolean forceDollared;
private final boolean completeAccessPrefix;
public static FieldItem getItem(FieldElement field, CompletionRequest request) {
return getItem(field, request, false);
}
public static FieldItem getItem(FieldElement field, CompletionRequest request, boolean forceDollared) {
return new FieldItem(field, request, forceDollared, false);
}
public static FieldItem getItem(FieldElement field, CompletionRequest request, boolean forceDollared, boolean completeAccessPrefix) {
return new FieldItem(field, request, forceDollared, completeAccessPrefix);
}
private FieldItem(FieldElement field, CompletionRequest request, boolean forceDollared, boolean completeAccessPrefix) {
super(field, null, request);
this.forceDollared = forceDollared;
this.completeAccessPrefix = completeAccessPrefix;
}
FieldElement getField() {
return (FieldElement) getElement();
}
@Override
public String getName() {
final FieldElement field = getField();
return field.getName(forceDollared || field.isStatic());
}
@Override
protected String getTypeName() {
Set<TypeResolver> types = getField().getInstanceTypes();
String typeName = types.isEmpty() ? "?" : types.size() > 1 ? Type.MIXED : "?"; //NOI18N
if (types.size() == 1) {
TypeResolver typeResolver = types.iterator().next();
if (typeResolver.isResolved()) {
QualifiedName qualifiedName = typeResolver.getTypeName(false);
if (qualifiedName != null) {
typeName = qualifiedName.toString();
}
}
}
return typeName;
}
@Override
public String getCustomInsertTemplate() {
if (completeAccessPrefix) {
Set<Modifier> modifiers = getModifiers();
String prefix = modifiers.contains(Modifier.STATIC) ? "self::" : "$this->"; // NOI18N
return prefix + getName();
}
return super.getCustomInsertTemplate();
}
}
static class TypeConstantItem extends PHPCompletionItem {
private final boolean completeAccessPrefix;
public static TypeConstantItem getItem(TypeConstantElement constant, CompletionRequest request) {
return getItem(constant, request, false);
}
public static TypeConstantItem getItem(TypeConstantElement constant, CompletionRequest request, boolean completeAccessPrefix) {
return new TypeConstantItem(constant, request, completeAccessPrefix);
}
private TypeConstantItem(TypeConstantElement constant, CompletionRequest request, boolean completeAccessPrefix) {
super(constant, request);
this.completeAccessPrefix = completeAccessPrefix;
}
TypeConstantElement getConstant() {
return (TypeConstantElement) getElement();
}
@Override
public ElementKind getKind() {
return ElementKind.CONSTANT;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.name(getKind(), true);
if (isDeprecated()) {
formatter.deprecated(true);
formatter.appendText(getName());
formatter.deprecated(false);
} else {
formatter.appendText(getName());
}
formatter.name(getKind(), false);
formatter.appendText(" "); //NOI18N
String value = getConstant().getValue();
formatter.type(true);
formatter.appendText(value != null ? value : "?"); //NOI18N
formatter.type(false);
return formatter.getText();
}
@Override
public String getName() {
return getConstant().getName();
}
@Override
public String getInsertPrefix() {
Completion.get().showToolTip();
return getName();
}
@Override
@NbBundle.Messages("MagicConstant=Magic Constant")
public String getRhsHtml(HtmlFormatter formatter) {
if (getConstant().isMagic()) {
formatter.appendText(Bundle.MagicConstant());
return formatter.getText();
}
return super.getRhsHtml(formatter);
}
@Override
public String getCustomInsertTemplate() {
if (completeAccessPrefix) {
return "self::" + getName(); // NOI18N
}
return super.getCustomInsertTemplate();
}
}
public static class MethodDeclarationItem extends MethodElementItem {
public static MethodDeclarationItem getDeclarationItem(final MethodElement methodElement, CompletionRequest request) {
return new MethodDeclarationItem(new FunctionElementItem(methodElement, request, methodElement.getParameters()));
}
public static MethodDeclarationItem forIntroduceHint(final MethodElement methodElement, CompletionRequest request) {
return new MethodDeclarationItem(new FunctionElementItem(methodElement, request, methodElement.getParameters())) {
@Override
protected String getFunctionBodyForTemplate() {
return "\n"; //NOI18N
}
};
}
public static MethodDeclarationItem forMethodName(final MethodElement methodElement, CompletionRequest request) {
return new MethodDeclarationItem(new FunctionElementItem(methodElement, request, methodElement.getParameters())) {
@Override
public String getCustomInsertTemplate() {
return insertOnlyMethodsName(request) ? super.getInsertPrefix() : super.getNameAndFunctionBodyForTemplate();
}
};
}
public static MethodDeclarationItem forIntroduceInterfaceHint(final MethodElement methodElement, CompletionRequest request) {
return new MethodDeclarationItem(new FunctionElementItem(methodElement, request, methodElement.getParameters())) {
@Override
protected String getBodyPart() {
return ";"; //NOI18N
}
};
}
private MethodDeclarationItem(FunctionElementItem functionItem) {
super(functionItem);
}
public MethodElement getMethod() {
return (MethodElement) getBaseFunctionElement();
}
@Override
public boolean isSmart() {
return !isMagic();
}
@Override
protected boolean emphasisName() {
return isMagic() ? false : super.emphasisName();
}
public boolean isMagic() {
return ((MethodElement) getBaseFunctionElement()).isMagic();
}
@Override
public String getCustomInsertTemplate() {
StringBuilder template = new StringBuilder();
String modifierStr = BodyDeclaration.Modifier.toString(getBaseFunctionElement().getFlags());
if (modifierStr.length() != 0) {
modifierStr = modifierStr.replace("abstract", "").trim(); //NOI18N
template.append(modifierStr);
}
template.append(" ").append("function"); //NOI18N
template.append(getNameAndFunctionBodyForTemplate());
return template.toString();
}
protected String getNameAndFunctionBodyForTemplate() {
StringBuilder template = new StringBuilder();
TypeNameResolver typeNameResolver = getBaseFunctionElement().getParameters().isEmpty() || request == null
? TypeNameResolverImpl.forNull()
: CodegenUtils.createSmarterTypeNameResolver(getBaseFunctionElement(), request.result.getModel(), request.anchor);
template.append(getBaseFunctionElement().asString(PrintAs.NameAndParamsDeclaration, typeNameResolver));
// #270237
FileObject fileObject = null;
if (request != null) {
// resquest is null if completion items are used in the IntroduceSuggestion hint
fileObject = request.result.getSnapshot().getSource().getFileObject();
PhpVersion phpVersion = getPhpVersion(fileObject);
if (phpVersion != null
&& phpVersion.compareTo(PhpVersion.PHP_70) >= 0) {
Collection<TypeResolver> returnTypes = getBaseFunctionElement().getReturnTypes();
if (returnTypes.size() == 1) {
String returnType = getBaseFunctionElement().asString(PrintAs.ReturnTypes);
if (StringUtils.hasText(returnType)) {
boolean nullableType = CodeUtils.isNullableType(returnType);
if (nullableType) {
returnType = returnType.substring(1);
}
if ("\\self".equals(returnType) // NOI18N
&& getBaseFunctionElement() instanceof TypeMemberElement) {
returnType = ((TypeMemberElement) getBaseFunctionElement()).getType().getFullyQualifiedName().toString();
}
template.append(": "); // NOI18N
if (nullableType) {
template.append(CodeUtils.NULLABLE_TYPE_PREFIX);
}
template.append(returnType);
}
}
}
}
template.append(getBodyPart());
return template.toString();
}
protected String getBodyPart() {
StringBuilder template = new StringBuilder();
template.append(" ").append("{\n"); //NOI18N
template.append(getFunctionBodyForTemplate()); //NOI18N
template.append("}"); //NOI18N
return template.toString();
}
/**
* @return body or null
*/
protected String getFunctionBodyForTemplate() {
StringBuilder template = new StringBuilder();
MethodElement method = (MethodElement) getBaseFunctionElement();
TypeElement type = method.getType();
if (isMagic() || type.isInterface() || method.isAbstract()) {
template.append("${cursor};\n"); //NOI18N
} else {
template.append("${cursor}parent::").append(getSignature().replace("&$", "$")).append(";\n"); //NOI18N
}
return template.toString();
}
private String getSignature() {
StringBuilder retval = new StringBuilder();
retval.append(getBaseFunctionElement().getName());
retval.append("(");
StringBuilder parametersInfo = new StringBuilder();
List<ParameterElement> parameters = getBaseFunctionElement().getParameters();
for (ParameterElement parameter : parameters) {
if (parametersInfo.length() > 0) {
parametersInfo.append(", "); //NOI18N
}
parametersInfo.append(parameter.getName());
}
retval.append(parametersInfo);
retval.append(")"); //NOI18N
return retval.toString();
}
@Override
@NbBundle.Messages({
"Generate=- generate",
"Override=- override"
})
public String getLhsHtml(HtmlFormatter formatter) {
StringBuilder sb = new StringBuilder();
sb.append(super.getLhsHtml(formatter));
sb.append(' ');
if (getMethod().isAbstract() || isMagic()) {
sb.append(Bundle.Generate());
} else {
sb.append(Bundle.Override());
}
return sb.toString();
}
@Override
@NbBundle.Messages("MagicMethod=Magic Method")
public String getRhsHtml(HtmlFormatter formatter) {
if (isMagic()) {
final String message = Bundle.MagicMethod();
formatter.appendText(message);
return formatter.getText();
}
return super.getRhsHtml(formatter);
}
}
static class ClassScopeKeywordItem extends KeywordItem {
private final String className;
ClassScopeKeywordItem(final String className, final String keyword, final CompletionRequest request) {
super(keyword, request);
this.className = className;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
if (keyword.startsWith("$")) { //NOI18N
if (className != null) {
formatter.type(true);
formatter.appendText(CodeUtils.isSyntheticTypeName(className) ? "{}" : className); // NOI18N
formatter.type(false);
}
formatter.appendText(" "); //NOI18N
}
return super.getLhsHtml(formatter);
}
}
static class KeywordItem extends PHPCompletionItem {
String keyword = null;
private static final List<String> CLS_KEYWORDS =
Arrays.asList(PHPCodeCompletion.PHP_CLASS_KEYWORDS);
KeywordItem(String keyword, CompletionRequest request) {
super(null, request);
this.keyword = keyword;
}
@Override
public String getName() {
return keyword;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.name(getKind(), true);
formatter.appendText(getName());
formatter.name(getKind(), false);
return formatter.getText();
}
@Override
public ElementKind getKind() {
return ElementKind.KEYWORD;
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
return null;
}
@Override
public ImageIcon getIcon() {
return KEYWORD_ICON;
}
@Override
public boolean isSmart() {
return CLS_KEYWORDS.contains(getName()) ? true : super.isSmart();
}
@Override
public String getInsertPrefix() {
return getName();
}
@Override
public String getCustomInsertTemplate() {
StringBuilder builder = new StringBuilder();
if (CLS_KEYWORDS.contains(getName())) {
scheduleShowingCompletion();
}
KeywordCompletionType type = PHPCodeCompletion.PHP_KEYWORDS.get(getName());
if (type == null) {
return getName();
}
CodeStyle codeStyle = CodeStyle.get(EditorRegistry.lastFocusedComponent().getDocument());
boolean appendSpace = true;
String name;
switch (type) {
case SIMPLE:
return null;
case ENDS_WITH_SPACE:
builder.append(getName());
builder.append(" ${cursor}"); //NOI18N
break;
case CURSOR_INSIDE_BRACKETS:
name = getName();
builder.append(name);
boolean appendBrackets = true;
switch (name) {
case "foreach": //NOI18N
case "for": //NOI18N
appendSpace = codeStyle.spaceBeforeForParen();
break;
case "if": //NOI18N
appendSpace = codeStyle.spaceBeforeIfParen();
break;
case "switch": //NOI18N
appendSpace = codeStyle.spaceBeforeSwitchParen();
break;
case "array": //NOI18N
if (request.context == CompletionContext.TYPE_NAME) {
// e.g. return type
appendBrackets = false;
appendSpace = false;
} else {
appendSpace = codeStyle.spaceBeforeArrayDeclParen();
}
break;
case "while": //NOI18N
appendSpace = codeStyle.spaceBeforeWhileParen();
break;
case "catch": //NOI18N
appendSpace = codeStyle.spaceBeforeCatchParen();
break;
default:
// no-op
}
if (appendSpace) {
builder.append(" "); //NOI18N
}
if (appendBrackets) {
builder.append("(${cursor})"); //NOI18N
}
break;
case ENDS_WITH_CURLY_BRACKETS:
name = getName();
builder.append(name);
switch (name) {
case "try": //NOI18N
appendSpace = codeStyle.spaceBeforeTryLeftBrace();
break;
case "do": //NOI18N
appendSpace = codeStyle.spaceBeforeDoLeftBrace();
break;
case "else": //NOI18N
appendSpace = codeStyle.spaceBeforeElseLeftBrace();
break;
default:
// no-op
}
if (appendSpace) {
builder.append(" "); //NOI18N
}
builder.append("{${cursor}}"); //NOI18N
if ("try".equals(name)) { //NOI18N
builder.append("catch (Exception $ex) {}"); //NOI18N
}
break;
case ENDS_WITH_BRACKETS_AND_CURLY_BRACKETS:
name = getName();
builder.append(name);
if (name.equals("elseif")) { //NOI18N
appendSpace = codeStyle.spaceBeforeIfParen();
}
if (appendSpace) {
builder.append(" "); //NOI18N
}
builder.append("(${cursor})"); //NOI18N
if (name.equals("elseif")) { //NOI18N
appendSpace = codeStyle.spaceBeforeIfLeftBrace();
}
if (appendSpace) {
builder.append(" "); //NOI18N
}
builder.append("{}"); //NOI18N
break;
case ENDS_WITH_SEMICOLON:
builder.append(getName());
CharSequence text = request.info.getSnapshot().getText();
int index = request.anchor + request.prefix.length();
if (index == text.length() || ';' != text.charAt(index)) { //NOI18N
builder.append(";"); //NOI18N
}
break;
case ENDS_WITH_COLON:
builder.append(getName());
builder.append(" ${cursor}:"); //NOI18N
break;
case CURSOR_BEFORE_ENDING_SEMICOLON:
builder.append(getName());
builder.append(" ${cursor};"); //NOI18N
break;
default:
assert false : type.toString();
break;
}
return builder.toString();
}
}
static class SuperGlobalItem extends PHPCompletionItem {
private String name;
public SuperGlobalItem(CompletionRequest request, String name) {
super(new PredefinedSymbolElement(name), request);
this.name = name;
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.name(getKind(), true); // <b> is added, so don't call formatter.emphasis(true)
formatter.appendText(getName());
formatter.name(getKind(), false); // </b> is added
return formatter.getText();
}
@Override
public String getName() {
return "$" + name; //NOI18N
}
@Override
public String getInsertPrefix() {
//todo insert array brackets for array vars
return getName();
}
@Override
public ElementKind getKind() {
return ElementKind.VARIABLE;
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
formatter.appendText(NbBundle.getMessage(PHPCompletionItem.class, "PHPPlatform"));
return formatter.getText();
}
public String getDocumentation() {
return null;
}
@Override
public ImageIcon getIcon() {
return KEYWORD_ICON;
}
}
static class NamespaceItem extends PHPCompletionItem {
private Boolean isSmart;
NamespaceItem(NamespaceElement namespace, CompletionRequest request, QualifiedNameKind generateAs) {
super(namespace, request, generateAs);
}
@Override
public String getInsertPrefix() {
// used for filtering purposes
return getName();
}
@Override
public String getCustomInsertTemplate() {
return super.getInsertPrefix();
}
@Override
public int getSortPrioOverride() {
return isSmart() ? -10001 : super.getSortPrioOverride();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.name(getKind(), true);
formatter.appendText(getName());
formatter.name(getKind(), false);
return formatter.getText();
}
@Override
public String getName() {
return getNamespaceElement().getName();
}
NamespaceElement getNamespaceElement() {
return (NamespaceElement) getElement();
}
@Override
public ElementKind getKind() {
return ElementKind.PACKAGE;
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
QualifiedName namespaceName = getNamespaceElement().getNamespaceName();
if (namespaceName != null && !namespaceName.isDefaultNamespace()) {
formatter.appendText(namespaceName.toString());
return formatter.getText();
}
return null;
}
@Override
public boolean isSmart() {
if (isSmart == null && getElement() instanceof AliasedElement) {
isSmart = true;
}
if (isSmart == null) {
QualifiedName namespaceName = getNamespaceElement().getNamespaceName();
isSmart = !(namespaceName == null || !namespaceName.isDefaultNamespace());
if (!isSmart) {
FileScope fileScope = request.result.getModel().getFileScope();
NamespaceScope namespaceScope = (fileScope != null)
? ModelUtils.getNamespaceScope(fileScope, request.anchor) : null;
if (namespaceScope != null) {
NamespaceElement ifq = getNamespaceElement();
LinkedList<String> segments = ifq.getFullyQualifiedName().getSegments();
QualifiedName fqna = QualifiedName.create(false, segments);
Collection<QualifiedName> relativeUses = VariousUtils.getRelativesToUses(namespaceScope, fqna);
for (QualifiedName qualifiedName : relativeUses) {
if (qualifiedName.getSegments().size() == 1) {
isSmart = true;
break;
}
}
if (!isSmart) {
relativeUses = VariousUtils.getRelativesToNamespace(namespaceScope, fqna);
for (QualifiedName qualifiedName : relativeUses) {
if (qualifiedName.getSegments().size() == 1) {
isSmart = true;
break;
}
}
}
}
}
}
return isSmart;
}
}
static class ConstantItem extends PHPCompletionItem {
ConstantItem(ConstantElement constant, CompletionRequest request) {
this(constant, request, null);
}
ConstantItem(ConstantElement constant, CompletionRequest request, QualifiedNameKind generateAs) {
super(constant, request, generateAs);
}
@Override
public String getName() {
String name = super.getName();
// #235450 TRUE, FALSE and NULL are defined with uppercase letters by default
if (isPlatform()) {
if ("TRUE".equals(name) || "FALSE".equals(name) || "NULL".equals(name)) { // NOI18N
if (OptionsUtils.autoCompletionUseLowercaseTrueFalseNull()) {
// default option is true
return super.getName().toLowerCase();
}
}
}
return super.getName();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
ElementHandle element = getElement();
assert element != null;
String value = ((ConstantElement) element).getValue();
formatter.name(getKind(), true);
if (emphasisName()) {
formatter.emphasis(true);
formatter.appendText(getName());
formatter.emphasis(false);
} else {
formatter.appendText(getName());
}
formatter.name(getKind(), false);
formatter.appendText(" "); //NOI18N
formatter.type(true);
formatter.appendText(value != null ? value : "?"); //NOI18N
formatter.type(false);
return formatter.getText();
}
protected boolean emphasisName() {
return true;
}
@Override
public ElementKind getKind() {
return ElementKind.CONSTANT;
}
}
static class TraitItem extends PHPCompletionItem {
private static final ImageIcon ICON = IconsUtils.getElementIcon(PhpElementKind.TRAIT);
TraitItem(TraitElement element, CompletionRequest request) {
super(element, request);
}
@Override
public ImageIcon getIcon() {
return ICON;
}
@Override
public ElementKind getKind() {
return ElementKind.CLASS;
}
}
static class ClassItem extends PHPCompletionItem {
private boolean endWithDoubleColon;
ClassItem(ClassElement clazz, CompletionRequest request, boolean endWithDoubleColon, QualifiedNameKind generateAs) {
super(clazz, request, generateAs);
this.endWithDoubleColon = endWithDoubleColon;
}
@Override
public ElementKind getKind() {
return ElementKind.CLASS;
}
@Override
public String getInsertPrefix() {
return getName();
}
@Override
public String getCustomInsertTemplate() {
final String superTemplate = super.getInsertPrefix();
if (endWithDoubleColon) {
StringBuilder builder = new StringBuilder();
builder.append(superTemplate);
boolean includeDoubleColumn = true;
if (EditorRegistry.lastFocusedComponent() != null) {
Document doc = EditorRegistry.lastFocusedComponent().getDocument();
int caret = EditorRegistry.lastFocusedComponent().getCaretPosition();
try {
if (caret + 2 < doc.getLength() && "::".equals(doc.getText(caret, 2))) { //NOI18N
includeDoubleColumn = false;
}
} catch (BadLocationException ex) {
// do nothing
}
}
if (includeDoubleColumn) {
builder.append("::");
}
builder.append("${cursor}"); //NOI18N
scheduleShowingCompletion();
return builder.toString();
} else if (CompletionContext.NEW_CLASS.equals(request.context)) {
scheduleShowingCompletion();
}
return superTemplate;
}
}
public static ImageIcon getInterfaceIcon() {
return InterfaceItem.icon();
}
static class InterfaceItem extends PHPCompletionItem {
private static final String PHP_INTERFACE_ICON = "org/netbeans/modules/php/editor/resources/interface.png"; //NOI18N
private static ImageIcon interfaceIcon = null;
private final boolean endWithDoubleColon;
InterfaceItem(InterfaceElement iface, CompletionRequest request, boolean endWithDoubleColon) {
super(iface, request);
this.endWithDoubleColon = endWithDoubleColon;
}
InterfaceItem(InterfaceElement iface, CompletionRequest request, QualifiedNameKind generateAs, boolean endWithDoubleColon) {
super(iface, request, generateAs);
this.endWithDoubleColon = endWithDoubleColon;
}
@Override
public ElementKind getKind() {
return ElementKind.CLASS;
}
private static ImageIcon icon() {
if (interfaceIcon == null) {
interfaceIcon = new ImageIcon(ImageUtilities.loadImage(PHP_INTERFACE_ICON));
}
return interfaceIcon;
}
@Override
public ImageIcon getIcon() {
return icon();
}
@Override
public String getInsertPrefix() {
return getName();
}
@Override
public String getCustomInsertTemplate() {
final String superTemplate = super.getInsertPrefix();
if (endWithDoubleColon) {
StringBuilder builder = new StringBuilder();
builder.append(superTemplate);
builder.append("::${cursor}"); //NOI18N
scheduleShowingCompletion();
return builder.toString();
}
return superTemplate;
}
}
static class VariableItem extends PHPCompletionItem {
VariableItem(VariableElement variable, CompletionRequest request) {
super(variable, request);
}
VariableElement getVariable() {
return (VariableElement) getElement();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
formatter.type(true);
formatter.appendText(getTypeName());
formatter.type(false);
formatter.appendText(" "); //NOI18N
formatter.name(getKind(), true);
formatter.appendText(getName());
formatter.name(getKind(), false);
return formatter.getText();
}
@Override
public ElementKind getKind() {
return ElementKind.VARIABLE;
}
@Override
public String getInsertPrefix() {
Completion.get().showToolTip();
return getName();
}
protected String getTypeName() {
Set<TypeResolver> types = getVariable().getInstanceTypes();
String typeName = types.isEmpty() ? "?" : types.size() > 1 ? Type.MIXED : "?"; //NOI18N
if (types.size() == 1) {
TypeResolver typeResolver = types.iterator().next();
if (typeResolver.isResolved()) {
QualifiedName qualifiedName = typeResolver.getTypeName(false);
if (qualifiedName != null) {
if (CodeUtils.isSyntheticTypeName(qualifiedName.getName())) {
// anonymous class
typeName = "{}"; // NOI18N
} else {
typeName = qualifiedName.toString();
}
}
}
}
return typeName;
}
}
@NbBundle.Messages("LBL_LANGUAGE_CONSTRUCT=Language Construct")
abstract static class LanguageConstructItem extends KeywordItem {
private static final String SORT_AFTER_KEYWORDS = "z"; // NOI18N
public LanguageConstructItem(String fncName, CompletionRequest request) {
super(fncName, request);
}
@Override
public String getRhsHtml(HtmlFormatter formatter) {
formatter.appendText(Bundle.LBL_LANGUAGE_CONSTRUCT());
return formatter.getText();
}
@Override
public String getSortText() {
return SORT_AFTER_KEYWORDS + super.getSortText();
}
protected void prependName(HtmlFormatter formatter) {
formatter.name(getKind(), true);
formatter.appendText(getName());
formatter.name(getKind(), false);
}
}
static class LanguageConstructWithQuotesItem extends LanguageConstructItem {
public LanguageConstructWithQuotesItem(String fncName, CompletionRequest request) {
super(fncName, request);
}
@Override
public String getCustomInsertTemplate() {
StringBuilder builder = new StringBuilder();
builder.append(getName());
builder.append(" '${cursor}';"); // NOI18N
return builder.toString();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
prependName(formatter);
formatter.appendText(" '';"); // NOI18N
return formatter.getText();
}
}
static class LanguageConstructWithParenthesesItem extends LanguageConstructItem {
public LanguageConstructWithParenthesesItem(String fncName, CompletionRequest request) {
super(fncName, request);
}
@Override
public String getCustomInsertTemplate() {
StringBuilder builder = new StringBuilder();
builder.append(getName());
builder.append("(${cursor})"); // NOI18N
return builder.toString();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
prependName(formatter);
formatter.appendText("()"); // NOI18N
return formatter.getText();
}
}
static class LanguageConstructWithSemicolonItem extends LanguageConstructItem {
public LanguageConstructWithSemicolonItem(String fncName, CompletionRequest request) {
super(fncName, request);
}
@Override
public String getCustomInsertTemplate() {
StringBuilder builder = new StringBuilder();
builder.append(getName());
builder.append(" ${cursor};"); // NOI18N
return builder.toString();
}
@Override
public String getLhsHtml(HtmlFormatter formatter) {
prependName(formatter);
formatter.appendText(" ;"); // NOI18N
return formatter.getText();
}
}
static class TagItem extends KeywordItem {
private int sortKey;
public TagItem(String tag, int sortKey, CompletionRequest request) {
super(tag, request);
this.sortKey = sortKey;
}
@Override
public String getSortText() {
return "" + sortKey + getName();
}
}
public static class CompletionRequest {
public int anchor;
public PHPParseResult result;
public ParserResult info;
public String prefix;
// used in special cases (e.g. in group use)
public String extraPrefix;
// whether to complete '()' (default: null == autodetection)
public Boolean insertOnlyMethodsName;
public String currentlyEditedFileURL;
public CompletionContext context;
ElementQuery.Index index;
}
private static void scheduleShowingCompletion() {
if (OptionsUtils.autoCompletionTypes()) {
service.schedule(new Runnable() {
@Override
public void run() {
Completion.get().showCompletion();
}
}, 750, TimeUnit.MILLISECONDS);
}
}
private static class PhpVersionChangeListener implements PropertyChangeListener {
private final WeakReference<FileObject> fileObjectReference;
public PhpVersionChangeListener(FileObject fileObject) {
this.fileObjectReference = new WeakReference<>(fileObject);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (PhpLanguageProperties.PROP_PHP_VERSION.equals(evt.getPropertyName())) {
FileObject fileObject = fileObjectReference.get();
if (fileObject != null) {
PROPERTIES_CACHE.save(fileObject, PhpLanguageProperties.forFileObject(fileObject));
}
}
}
}
}