/* * Copyright 2013-2015 Grzegorz Ligas <ligasgr@gmail.com> and other contributors * (see the CONTRIBUTORS file). * * 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 org.intellij.xquery.completion.function.parameters; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.parameterInfo.CreateParameterInfoContext; import com.intellij.lang.parameterInfo.ParameterInfoContext; import com.intellij.lang.parameterInfo.ParameterInfoHandler; import com.intellij.lang.parameterInfo.ParameterInfoUIContext; import com.intellij.lang.parameterInfo.ParameterInfoUtils; import com.intellij.lang.parameterInfo.UpdateParameterInfoContext; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiReference; import com.intellij.psi.ResolveResult; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.xquery.completion.function.BuiltInFunctionSignature; import org.intellij.xquery.psi.XQueryArgumentList; import org.intellij.xquery.psi.XQueryFile; import org.intellij.xquery.psi.XQueryFunctionCall; import org.intellij.xquery.psi.XQueryFunctionDecl; import org.intellij.xquery.psi.XQueryFunctionName; import org.intellij.xquery.psi.XQueryParam; import org.intellij.xquery.psi.XQueryTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import static org.intellij.xquery.util.StringUtils.compressWhitespaces; public class XQueryParameterInfoHandler implements ParameterInfoHandler<XQueryArgumentList, Object> { @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 null; } @Nullable @Override public XQueryArgumentList findElementForParameterInfo(CreateParameterInfoContext context) { return getArgumentList(context); } @Nullable @Override public XQueryArgumentList findElementForUpdatingParameterInfo(UpdateParameterInfoContext context) { return getArgumentList(context); } private XQueryArgumentList getArgumentList(ParameterInfoContext context) { PsiElement at = context.getFile().findElementAt(context.getOffset()); return PsiTreeUtil.getParentOfType(at, XQueryArgumentList.class); } @Override public void showParameterInfo(@NotNull XQueryArgumentList args, CreateParameterInfoContext context) { XQueryFunctionCall functionCall = PsiTreeUtil.getParentOfType(args, XQueryFunctionCall.class); if (functionCall != null) { addItemsToShow(context, functionCall); context.showHint(args, args.getTextRange().getStartOffset(), this); } } private void addItemsToShow(CreateParameterInfoContext context, XQueryFunctionCall functionCall) { ResolveResult[] resolveResults = getFunctionWithAllArities(functionCall); List<XQueryFunctionDecl> functionDeclarations = extractFunctionDeclarations(resolveResults); if (functionDeclarations.size() > 0) { Collections.sort(functionDeclarations, getParameterListSizeComparator()); context.setItemsToShow(ArrayUtil.toObjectArray(functionDeclarations)); } else { XQueryFile file = (XQueryFile) functionCall.getContainingFile(); String name = functionCall.getFunctionName().getLocalNameText(); String prefix = functionCall.getFunctionName().getPrefixText(); String namespace = file.mapFunctionPrefixToNamespace(prefix); context.setItemsToShow(ArrayUtil.toObjectArray(getFunctionsSignatures(file, name, namespace))); } } private Collection<BuiltInFunctionSignature> getFunctionsSignatures(XQueryFile file, String name, String namespace) { return file.getFunctionsSignatures(namespace, name); } private Comparator<XQueryFunctionDecl> getParameterListSizeComparator() { return new Comparator<XQueryFunctionDecl>() { @Override public int compare(XQueryFunctionDecl lhs, XQueryFunctionDecl rhs) { final int lhsSize = lhs.getParamList() != null ? lhs.getParamList().getParamList().size() : 0; final int rhsSize = rhs.getParamList() != null ? rhs.getParamList().getParamList().size() : 0; return Integer.signum(lhsSize - rhsSize); } }; } private List<XQueryFunctionDecl> extractFunctionDeclarations(ResolveResult[] resolveResults) { List<XQueryFunctionDecl> functionDeclarations = new ArrayList<XQueryFunctionDecl>(); for (ResolveResult result : resolveResults) { PsiElement element = result.getElement(); if (element instanceof XQueryFunctionName) { functionDeclarations.add((XQueryFunctionDecl) element.getParent()); } } return functionDeclarations; } private ResolveResult[] getFunctionWithAllArities(XQueryFunctionCall functionCall) { PsiReference reference = functionCall.getReference(); return ((PsiPolyVariantReference) reference).multiResolve(true); } @Override public void updateParameterInfo(@NotNull XQueryArgumentList place, UpdateParameterInfoContext context) { context.setCurrentParameter(ParameterInfoUtils.getCurrentParameterIndex(place.getNode(), context.getOffset(), XQueryTypes.COMMA)); } @Nullable @Override public String getParameterCloseChars() { return ParameterInfoUtils.DEFAULT_PARAMETER_CLOSE_CHARS; } @Override public boolean tracksParameterIndex() { return true; } @Override public void updateUI(Object p, ParameterInfoUIContext context) { if (p == null) { context.setUIComponentEnabled(false); return; } int index = context.getCurrentParameterIndex(); ParameterPresentation presentation = null; if (p instanceof XQueryFunctionDecl) { presentation = buildUserFunctionPresentation((XQueryFunctionDecl) p, index); } else if (p instanceof BuiltInFunctionSignature) { presentation = buildBuiltInFunctionPresentation((BuiltInFunctionSignature) p, index); } context.setupUIComponentPresentation(presentation.text, presentation.start, presentation.end, presentation.disabled, false, true, context.getDefaultParameterColor()); } private ParameterPresentation buildBuiltInFunctionPresentation(BuiltInFunctionSignature functionSignature, int index) { StringBuilder builder = new StringBuilder(); int start = 0; int end = 0; boolean disabled = false; String arguments = functionSignature.getArguments(); builder.append(arguments); for (int i = 0; i < index && start != - 1; ++ i) { start = arguments.indexOf(',', start + 1); } if (start == - 1) { disabled = true; } else { end = arguments.indexOf(',', start + 1); end = (end == - 1 ? arguments.length() : end); } return new ParameterPresentation(builder.toString(), start, end, disabled); } private ParameterPresentation buildUserFunctionPresentation(XQueryFunctionDecl functionDecl, int index) { StringBuilder builder = new StringBuilder(); int start = 0; int end = 0; boolean disabled; List<XQueryParam> args = functionDecl.getParamList() != null ? functionDecl.getParamList().getParamList() : ContainerUtil.<XQueryParam>emptyList(); for (int i = 0; i < args.size(); i++) { if (i != 0) builder.append(", "); if (index == i) start = builder.length(); builder.append(compressWhitespaces(args.get(i).getText())); if (index == i) end = builder.length(); } disabled = index >= args.size(); return new ParameterPresentation(builder.toString(), start, end, disabled); } private class ParameterPresentation { String text; int start; int end; boolean disabled = false; public ParameterPresentation(String text, int start, int end, boolean disabled) { if (text.length() == 0) { this.text = "<no parameters>"; } else { this.text = text; } this.start = start; this.end = end; this.disabled = disabled; } } }