/*
* Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan
*
* Licensed 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 com.goide.editor;
import com.goide.GoTypes;
import com.goide.psi.*;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.lang.parameterInfo.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
public class GoParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<GoArgumentList, Object, GoExpression> {
@NotNull
@Override
public GoExpression[] getActualParameters(@NotNull GoArgumentList o) {
return ArrayUtil.toObjectArray(o.getExpressionList(), GoExpression.class);
}
@NotNull
@Override
public IElementType getActualParameterDelimiterType() {
return GoTypes.COMMA;
}
@NotNull
@Override
public IElementType getActualParametersRBraceType() {
return GoTypes.RPAREN;
}
@NotNull
@Override
public Set<Class> getArgumentListAllowedParentClasses() {
return ContainerUtil.newHashSet();
}
@NotNull
@Override
public Set<Class> getArgListStopSearchClasses() {
return ContainerUtil.newHashSet();
}
@NotNull
@Override
public Class<GoArgumentList> getArgumentListClass() {
return GoArgumentList.class;
}
@Override
public boolean couldShowInLookup() {
return true;
}
@Nullable
@Override
public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@Nullable
@Override
public Object[] getParametersForDocumentation(Object p, ParameterInfoContext context) {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@Nullable
@Override
public GoArgumentList findElementForParameterInfo(@NotNull CreateParameterInfoContext context) {
// todo: see ParameterInfoUtils.findArgumentList
return getList(context);
}
@Nullable
private static GoArgumentList getList(@NotNull ParameterInfoContext context) {
PsiElement at = context.getFile().findElementAt(context.getOffset());
return PsiTreeUtil.getParentOfType(at, GoArgumentList.class);
}
@Override
public void showParameterInfo(@NotNull GoArgumentList argList, @NotNull CreateParameterInfoContext context) {
PsiElement parent = argList.getParent();
if (!(parent instanceof GoCallExpr)) return;
GoFunctionType type = findFunctionType(((GoCallExpr)parent).getExpression().getGoType(null));
if (type != null) {
context.setItemsToShow(new Object[]{type});
context.showHint(argList, argList.getTextRange().getStartOffset(), this);
}
}
@Nullable
private static GoFunctionType findFunctionType(@Nullable GoType type) {
if (type instanceof GoFunctionType || type == null) return (GoFunctionType)type;
GoType base = type.getUnderlyingType();
return base instanceof GoFunctionType ? (GoFunctionType)base : null;
}
@Nullable
@Override
public GoArgumentList findElementForUpdatingParameterInfo(@NotNull UpdateParameterInfoContext context) {
return getList(context);
}
@Override
public void updateParameterInfo(@NotNull GoArgumentList list, @NotNull UpdateParameterInfoContext context) {
context.setCurrentParameter(ParameterInfoUtils.getCurrentParameterIndex(list.getNode(), context.getOffset(), GoTypes.COMMA));
}
@Nullable
@Override
public String getParameterCloseChars() {
return ",(";
}
@Override
public boolean tracksParameterIndex() {
return true;
}
@Override
public void updateUI(@Nullable Object p, @NotNull ParameterInfoUIContext context) {
updatePresentation(p, context);
}
static String updatePresentation(@Nullable Object p, @NotNull ParameterInfoUIContext context) {
if (p == null) {
context.setUIComponentEnabled(false);
return null;
}
GoSignature signature = p instanceof GoSignatureOwner ? ((GoSignatureOwner)p).getSignature() : null;
if (signature == null) return null;
GoParameters parameters = signature.getParameters();
List<String> parametersPresentations = getParameterPresentations(parameters, PsiElement::getText);
StringBuilder builder = new StringBuilder();
int start = 0;
int end = 0;
if (!parametersPresentations.isEmpty()) {
// Figure out what particular presentation is actually selected. Take in
// account possibility of the last variadic parameter.
int selected = isLastParameterVariadic(parameters.getParameterDeclarationList())
? Math.min(context.getCurrentParameterIndex(), parametersPresentations.size() - 1)
: context.getCurrentParameterIndex();
for (int i = 0; i < parametersPresentations.size(); ++i) {
if (i != 0) {
builder.append(", ");
}
if (i == selected) {
start = builder.length();
}
builder.append(parametersPresentations.get(i));
if (i == selected) {
end = builder.length();
}
}
}
else {
builder.append(CodeInsightBundle.message("parameter.info.no.parameters"));
}
return context.setupUIComponentPresentation(builder.toString(), start, end, false, false, false, context.getDefaultParameterColor());
}
/**
* Creates a list of parameter presentations. For clarity we expand parameters declared as `a, b, c int` into `a int, b int, c int`.
*/
@NotNull
public static List<String> getParameterPresentations(@NotNull GoParameters parameters,
@NotNull Function<PsiElement, String> typePresentationFunction) {
List<GoParameterDeclaration> paramDeclarations = parameters.getParameterDeclarationList();
List<String> paramPresentations = ContainerUtil.newArrayListWithCapacity(2 * paramDeclarations.size());
for (GoParameterDeclaration paramDeclaration : paramDeclarations) {
boolean isVariadic = paramDeclaration.isVariadic();
List<GoParamDefinition> paramDefinitionList = paramDeclaration.getParamDefinitionList();
for (GoParamDefinition paramDefinition : paramDefinitionList) {
String separator = isVariadic ? " ..." : " ";
paramPresentations.add(paramDefinition.getText() + separator + typePresentationFunction.fun(paramDeclaration.getType()));
}
if (paramDefinitionList.isEmpty()) {
String separator = isVariadic ? "..." : "";
paramPresentations.add(separator + typePresentationFunction.fun(paramDeclaration.getType()));
}
}
return paramPresentations;
}
private static boolean isLastParameterVariadic(@NotNull List<GoParameterDeclaration> declarations) {
GoParameterDeclaration lastItem = ContainerUtil.getLastItem(declarations);
return lastItem != null && lastItem.isVariadic();
}
}