Skip to content

Commit

Permalink
Issue #32 - Built-in function completion - add parameter info handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
ligasgr committed Dec 8, 2013
1 parent 06d4c30 commit 4b86c21
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 30 deletions.
Expand Up @@ -42,7 +42,7 @@ public String getReturnType() {
}

public String getArguments() {
return "(" + (arguments != null ? arguments : "") + ")";
return arguments != null ? arguments : "";
}

public String getName() {
Expand Down
Expand Up @@ -16,8 +16,6 @@

package org.intellij.xquery.completion.function;

import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import org.intellij.xquery.icons.XQueryIcons;
Expand All @@ -33,19 +31,14 @@ public static LookupElement convert(BuiltInFunctionSignature functionSignature,
.withIcon(XQueryIcons.FUNCTION_ICON)
.withTailText(getTailText(functionSignature), true)
.withTypeText(getTypeText(functionSignature))
.withInsertHandler(new ParenthesesInsertHandler<LookupElement>() {
@Override
protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
return true;
}
});
.withInsertHandler(new XQueryFunctionInsertHandler());
}

private static String getTypeText(BuiltInFunctionSignature functionSignature) {
return functionSignature.getReturnType();
}

private static String getTailText(BuiltInFunctionSignature functionSignature) {
return functionSignature.getArguments();
return "(" + functionSignature.getArguments() + ")";
}
}
Expand Up @@ -19,7 +19,9 @@
import com.intellij.util.containers.MultiMap;
import org.intellij.xquery.reference.namespace.XQueryPredeclaredNamespace;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;

/**
Expand Down Expand Up @@ -60,6 +62,14 @@ protected Collection<BuiltInFunctionSignature> createCollection() {
fnMap.putValue(ns("fn"), bif("fn", "compare", 2, "$comparand1 as xs:string?, $comparand2 as xs:string?", "xs:integer?"));
fnMap.putValue(ns("fn"), bif("fn", "compare", 3, "$comparand1 as xs:string?, $comparand2 as xs:string?, $collation as xs:string", "xs:integer?"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 2, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 3, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 4, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 5, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 6, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?, $arg6 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 7, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?, $arg6 as xs:anyAtomicType?, $arg7 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 8, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?, $arg6 as xs:anyAtomicType?, $arg7 as xs:anyAtomicType?, $arg8 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 9, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?, $arg6 as xs:anyAtomicType?, $arg7 as xs:anyAtomicType?, $arg8 as xs:anyAtomicType?, $arg9 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "concat", 10, "$arg1 as xs:anyAtomicType?, $arg2 as xs:anyAtomicType?, $arg3 as xs:anyAtomicType?, $arg4 as xs:anyAtomicType?, $arg5 as xs:anyAtomicType?, $arg6 as xs:anyAtomicType?, $arg7 as xs:anyAtomicType?, $arg8 as xs:anyAtomicType?, $arg9 as xs:anyAtomicType?, $arg10 as xs:anyAtomicType?", "xs:string"));
fnMap.putValue(ns("fn"), bif("fn", "contains", 2, "$arg1 as xs:string?, $arg2 as xs:string?", "xs:boolean"));
fnMap.putValue(ns("fn"), bif("fn", "contains", 3, "$arg1 as xs:string?, $arg2 as xs:string?, $collation as xs:string", "xs:boolean"));
fnMap.putValue(ns("fn"), bif("fn", "count", 1, "$arg as item()*", "xs:integer"));
Expand Down Expand Up @@ -267,4 +277,14 @@ private static BuiltInFunctionSignature bif(String prefix, String name, int arit
public static Collection<BuiltInFunctionSignature> getFunctionsSignatures(String namespace) {
return fnMap.get(namespace);
}

public static Collection<BuiltInFunctionSignature> getFunctionsSignatures(String namespace, String name) {
final List<BuiltInFunctionSignature> signatures = new ArrayList<BuiltInFunctionSignature>();
for (BuiltInFunctionSignature signature : fnMap.get(namespace)) {
if (name.equals(signature.getName())) {
signatures.add(signature);
}
}
return signatures;
}
}
Expand Up @@ -16,8 +16,6 @@

package org.intellij.xquery.completion.function;

import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import org.intellij.xquery.icons.XQueryIcons;
Expand All @@ -34,12 +32,7 @@ public static LookupElement convert(XQueryFunctionDecl functionDeclaration, Stri
.withIcon(XQueryIcons.FUNCTION_ICON)
.withTailText(getTailText(functionDeclaration), true)
.withTypeText(getTypeText(functionDeclaration))
.withInsertHandler(new ParenthesesInsertHandler<LookupElement>() {
@Override
protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
return true;
}
});
.withInsertHandler(new XQueryFunctionInsertHandler());
}

private static String getTypeText(XQueryFunctionDecl functionDeclaration) {
Expand Down
@@ -0,0 +1,42 @@
/*
* Copyright 2013 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;

import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;

/**
* User: ligasgr
* Date: 08/12/13
* Time: 19:57
*/
public class XQueryFunctionInsertHandler extends ParenthesesInsertHandler<LookupElement> {

@Override
public void handleInsert(InsertionContext context, LookupElement item) {
super.handleInsert(context, item);
AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(context.getEditor(),
item.getPsiElement());
}

@Override
protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) {
return true;
}
}
Expand Up @@ -13,6 +13,7 @@
* 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;
Expand All @@ -29,7 +30,10 @@
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.completion.function.BuiltInFunctionTable;
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;
Expand All @@ -53,7 +57,7 @@
public class XQueryParameterInfoHandler implements ParameterInfoHandler<XQueryArgumentList, Object> {
@Override
public boolean couldShowInLookup() {
return false;
return true;
}

@Nullable
Expand Down Expand Up @@ -94,13 +98,17 @@ public void showParameterInfo(@NotNull XQueryArgumentList args, CreateParameterI
}
}

private void addItemsToShow(CreateParameterInfoContext context, XQueryFunctionCall
functionCall) {
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 {
String name = functionCall.getFunctionName().getLocalNameText();
String prefix = functionCall.getFunctionName().getPrefixText();
String namespace = ((XQueryFile) functionCall.getContainingFile()).mapFunctionPrefixToNamespace(prefix);
context.setItemsToShow(ArrayUtil.toObjectArray(BuiltInFunctionTable.getFunctionsSignatures(namespace, name)));
}
}

Expand Down Expand Up @@ -158,18 +166,42 @@ public void updateUI(Object p, ParameterInfoUIContext context) {
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();
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(", ");
Expand Down
Expand Up @@ -28,20 +28,41 @@
public class XQueryParameterInfoHandlerTest extends BaseFunctionalTestCase {

private static final String TWO_PARAMETERS_FUNCTION = "declare function fun($a, $b){()};";
private static final String ONE_AND_TWO_PARAMETERS_FUNCTION = "declare function fun($a){()};" + TWO_PARAMETERS_FUNCTION;

public void testShouldHighlightFirstParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fun(<caret>)", 0);
public void testShouldNotPointToAnyParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fu<caret>n()", -1, 0);
}

public void testShouldHighlightSecondParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fun('a', <caret>)", 1);
public void testShouldPointToFirstParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fun(<caret>)", 0, 1);
}

public void testShouldHighlightNothing() {
doTest(TWO_PARAMETERS_FUNCTION + "fun('a', 'b', <caret>)", 2);
public void testShouldPointToFirstParameterAndShowTwoItems() {
doTest(ONE_AND_TWO_PARAMETERS_FUNCTION + "fun(<caret>)", 0, 2);
}

private void doTest(String text, int highlightedParameterIndex) {
public void testShouldPointToSecondParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fun('a', <caret>)", 1, 1);
}

public void testShouldPointToSecondParameterAndShowTwoItems() {
doTest(ONE_AND_TWO_PARAMETERS_FUNCTION + "fun('a', <caret>)", 1, 2);
}

public void testShouldPointToThirdParameter() {
doTest(TWO_PARAMETERS_FUNCTION + "fun('a', 'b', <caret>)", 2, 1);
}

public void testShouldPointToFirstParameterForBuiltInFunctionAndShowOneItem() {
doTest("fn:true(<caret>)", 0, 1);
}

public void testShouldPointToFirstParameterForBuiltInFunctionAndShowTwoItems() {
doTest("fn:starts-with(<caret>)", 0, 2);
}

private void doTest(String text, int highlightedParameterIndex, int shownItems) {
myFixture.configureByText("a.xq", text);
final XQueryParameterInfoHandler parameterInfoHandler = new XQueryParameterInfoHandler();
final CreateParameterInfoContext createContext = new FakeCreateParameterInfoContext(myFixture.getEditor(), myFixture.getFile());
Expand All @@ -52,7 +73,7 @@ private void doTest(String text, int highlightedParameterIndex) {
parameterInfoHandler.showParameterInfo(list, createContext);
Object[] itemsToShow = createContext.getItemsToShow();
assertNotNull(itemsToShow);
assertTrue(itemsToShow.length > 0);
assertEquals(shownItems, itemsToShow.length);
}
FakeUpdateParameterInfoContext updateContext = new FakeUpdateParameterInfoContext(myFixture.getEditor(), myFixture.getFile());
final XQueryArgumentList element = parameterInfoHandler.findElementForUpdatingParameterInfo(updateContext);
Expand Down

0 comments on commit 4b86c21

Please sign in to comment.