/*
* Copyright 2012-2015 Sergey Ignatov
*
* 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.erlang.completion;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.erlang.psi.impl.ErlangPsiImplUtil;
import java.util.List;
import java.util.Set;
public class ErlangCompletionTest extends ErlangCompletionTestBase {
public void testKeywords1() { doTestInclude("-<caret>", "module", "record", "define"); }
public void testVariablesFromDefinition() { doTestInclude("foo(A, B, C)-> <caret>", "A", "B", "C"); }
public void testVariablesFromBody() { doTestInclude("foo(A, B, C)-> D=1, <caret>", "A", "B", "C", "D"); }
public void testFunctions() {
doTestInclude(
"foo() -> ok.\n" +
"buzz() -> ok.\n" +
"bar(A)-> <caret>", "foo", "buzz");
}
public void testFunctionCompletionInTypedList() {
doTestInclude("foo() -> ok. -record(state, {first = <caret>}).",
"begin", "try", "fun", "if",
"node", "pid_to_list", "spawn", "binary_to_list",
"foo");
}
public void testRecords() {
doTestInclude(
"-record(foo, {id}).\n" +
"-record(buz, {id}).\n" +
"bar(A)-> A#<caret>", "foo", "buz");
}
public void testRecordFields() {
doTestEquals(
"-record(foo, {id, two}).\n" +
"bar(A)-> #foo{<caret>}", "id", "two");
}
public void testRecordFields2() {
doTestEquals(
"-record(foo, {id, two}).\n" +
"bar(A)-> #foo{two=1,<caret>}", "id", "two");
}
public void testRecordFields3() {
doTestInclude(
"-record(foo, {id, two}).\n" +
"bar(A, B)-> #foo{two= <caret>}", "A", "B");
}
public void testRecordFields4() {
doTestInclude(
"-record(foo, {id, two}).\n" +
"bar(A, B)-> #foo{two=<caret>}", "A", "B");
}
public void testRecordFields5() {
doCheckResult(
"-record(foo, {id, two}).\n" +
"bar(A, B)-> A#foo.tw<caret>",
"-record(foo, {id, two}).\n" +
"bar(A, B)-> A#foo.two");
}
public void testRecordFields6() {
doCheckResult(
"-record(foo, {id, two}).\n" +
"bar(A, B)-> A#foo{tw<caret>}",
"-record(foo, {id, two}).\n" +
"bar(A, B)-> A#foo{two = }");
}
public void testMacros() {
doTestInclude(
"-define(foo, 1).\n" +
"-define(buz, 1).\n" +
"bar(A)-> ?<caret>", "foo", "buz");
}
public void testTypesInRecords() {
doTestInclude(
"-type foo() :: atom().\n" +
"-type buz() :: string().\n" +
"-record(rec, {id :: <caret>}).", "foo", "buz");
}
public void testBuiltInTypesInRecords() {
doTestInclude(
"-type foo() :: atom().\n" +
"-type buz() :: string().\n" +
"-record(rec, {id :: <caret>}).",
ArrayUtilRt.toStringArray(ErlangPsiImplUtil.BUILT_IN_TYPES)
);
}
public void testTypesInSpec() {
doTestInclude(
"-type foo() :: atom().\n" +
"-type buz() :: string().\n" +
"-spec my_fun(<caret>)", "foo", "buz", "atom", "no_return");
}
public void testTypesInTypeDeclaration() {
doTestInclude(
"-type foo() :: <caret>atom().\n" +
"-type buz() :: string().\n" +
"-type tes() :: <caret>)", "foo", "buz", "atom", "no_return");
}
public void testBif() {
doTestInclude("foo() -> <caret>", "is_function", "is_record", "universaltime_to_posixtime");
}
public void testBifFromModules() {
doTestInclude("foo() -> lists:<caret>", "member", "reverse", "keysearch");
}
public void testMultiModule() {
myFixture.configureByFiles("multi-module/a.erl");
myFixture.configureByFile("multi-module/b.erl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.EQUALS, "bar", "bar", "foo", "foo",
"module_info", "module_info");
// means "bar/1", "bar/0", "foo/1", "foo/0", "module_info/0", "module_info/1"
}
public void testBifImport() {
doTestInclude("-import(math, [<caret>]).", "sin", "sqrt");
}
public void testBifImport2() {
doTestInclude("-import(math, [sin/1, sqrt/1]).\n" +
"foo() -> <caret>", "sin", "sqrt");
}
public void test182() {
doTestInclude("test() -> <caret>\n" +
"ok.\n" +
"my_local_function() -> not_so_ok.",
"my_local_function");
}
public void testNoVariableDuplicates() {
myFixture.configureByText("a.erl",
"foo() ->\n" +
" case {1, 1} of\n" +
" {A, A} -> <caret>\n" +
" end.");
myFixture.complete(CompletionType.BASIC, 1);
List<String> stringList = myFixture.getLookupElementStrings();
assertNotNull(stringList);
List<String> vars = ContainerUtil.filter(stringList, s -> s.equals("A"));
assertSize(1, vars);
}
public void testIncludeLib() { doCheckResult("-include_<caret>", "-include_lib(\"<caret>\")."); }
public void testInclude() { doCheckResult("-inclu<caret>", "-include(\"<caret>\").", '('); }
public void testExport() { doCheckResult("-exp<caret>", "-export([<caret>]).", '('); }
public void testExportType() { doCheckResult("-export_t<caret>", "-export_type([<caret>])."); }
public void testOptionalCallbacks() { doCheckResult("-optional_c<caret>", "-optional_callbacks([<caret>])."); }
public void testBehaviour() { doCheckResult("-behaviou<caret>", "-behaviour(<caret>)."); }
public void testBehavior() { doCheckResult("-behavior<caret>", "-behavior(<caret>)."); }
public void testAppFile() {
doAppFileCheckResult("{ap<caret>}.", "{application}.");
}
public void testAppFileBadPosition() {
doAppFileCheckResult("{application, <caret>}.", "{application, <caret>}.");
}
public void testAppFileParameter() {
doAppFileCheckResult("{application, name, [{re<caret>}]}.", "{application, name, [{registered<caret>}]}.");
}
public void testAppFileBadParameterPosition() {
doAppFileCheckResult("{application, aaa, [{custom, star<caret>}]}.",
"{application, aaa, [{custom, star<caret>}]}.");
}
public void testAppFileKeywordNotInTuple() {
doAppFileCheckResult("{application, name, [<caret>]}.", "{application, name, [<caret>]}.");
}
public void testAppFileBadListPosition() {
doAppFileCheckResult("{application, [{<caret>}]}.", "{application, [{<caret>}]}.");
}
public void testAppFileBadKeyword() {
doAppFileCheckResult("{regis<caret>}.", "{regis<caret>}.");
}
public void testAppFileKeywordsAreDistinct() {
Set<String> set = ContainerUtil.newHashSet(ErlangAppCompletionContributor.KEYWORDS);
assertTrue(set.size() == ErlangAppCompletionContributor.KEYWORDS.size());
}
public void testSingleQuotes() {
myFixture.configureByText("a.erl", "");
myFixture.type("'");
myFixture.checkResult("''");
}
public void testSingleQuotesWithText() {
myFixture.configureByText("a.erl", "");
myFixture.type("'Hello ");
myFixture.type("'");
myFixture.checkResult("'Hello '");
}
public void testExportFunction() {
doCheckResult("-export([<caret>]). foo(A, B, C) -> ok.", "-export([foo/3<caret>]). foo(A, B, C) -> ok.", Lookup.COMPLETE_STATEMENT_SELECT_CHAR);
}
public void testExportFunctionStartedTyping() {
doCheckResult("-export([f<caret>]). foo(A, B, C) -> ok.", "-export([foo/3<caret>]). foo(A, B, C) -> ok.");
}
public void testLager() {
doTestInclude("foo() -> lager:<caret>", "debug", "info", "notice", "warning", "error", "critical", "alert", "emergency");
}
public void testImportModule() {
myFixture.configureByFiles("multi-module/a.erl");
myFixture.configureByFile("multi-module/b.erl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.EQUALS, "bar", "bar", "foo", "foo",
"module_info", "module_info");
// means "bar/1", "bar/0", "foo/1", "foo/0", "module_info/0", "module_info/1"
}
public void testModuleCompletionContainsModule() {
myFixture.configureByFiles("module-completion/use_module.erl", "module-completion/test_module.erl");
doTestVariantsInner(CompletionType.BASIC, 2, CheckType.INCLUDES, "test_module");
}
public void testModuleCompletionContainsFunctions() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestInclude("foo() -> fake_mod<caret>", "fake_module:bar");
}
public void testModuleCompletionExcludeFunctions() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestVariants("foo() -> fake_mod<caret>", CompletionType.BASIC, 1, CheckType.EXCLUDES, "far", "fake_module:far");
}
public void testFunctionCompletionByPartialName() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestInclude("foo() -> fmba<caret>", "fake_module:bar");
}
public void testFunctionCompletionExcludeByPartialName() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestVariants("foo() -> fmba<caret>", CompletionType.BASIC, 1, CheckType.EXCLUDES,
"tar", "fake_module:tar",
"far", "fake_module:far");
}
public void testFunctionCompletionByPartialNameWithColon() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestInclude("foo() -> fm:ba<caret>", "fake_module:bar");
}
public void testFunctionCompletionCheckFirst() {
myFixture.configureByFiles("module-completion/fake_module.erl");
myFixture.configureByText("a.erl", "bar() -> ok. foo() -> bar<caret>");
myFixture.complete(CompletionType.BASIC, 1);
List<String> compList = myFixture.getLookupElementStrings();
assertNotNull(compList);
assertEquals(compList.get(0), "bar");
}
public void testFunctionExpandByPartialName() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doCheckResult("foo() -> fmta<caret>", "foo() -> fake_module:tar()<caret>");
}
public void testModuleNameIsMatchedFromTextBeforeColon() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestEquals("foo() -> famo:<caret>",
"fake_module:bar", "fake_module:bar", "fake_module:tar", "module_info", "module_info");
// means "fake_module:bar/0", "fake_module:bar/1", "fake_module:tar/0", "module_info/0", "module_info/1"
}
public void testModuleNameIsMatchedFromTextBeforeColonAtComma() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestEquals("foo() -> famo:<caret>, ok.",
"fake_module:bar", "fake_module:bar", "fake_module:tar", "module_info", "module_info");
// means "fake_module:bar/0", "fake_module:bar/1", "fake_module:tar/0", "module_info/0", "module_info/1"
}
public void testModuleNameIsMatchedFromTextBeforeColonAtIncompleteClause() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestEquals("foo() -> famo:<caret> ok.",
"fake_module:bar", "fake_module:bar", "fake_module:tar", "module_info", "module_info");
// means "fake_module:bar/0", "fake_module:bar/1", "fake_module:tar/0", "module_info/0", "module_info/1"
}
public void testModuleNameIsMatchedFromTextBeforeColonWithPartialMatch() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestEquals("foo() -> famo:ba<caret>ckend", "fake_module:bar", "fake_module:bar");
// means "fake_module:bar/0", "fake_module:bar/1"
}
public void testModuleFunctionCompletionForEmptyText() {
myFixture.configureByFiles("module-completion/fake_module.erl");
doTestInclude("foo() -> <caret>.", "fake_module", "fake_module:bar", "fake_module:tar", "finish_after_on_load");
}
public void testModuleFunctionCompletionQuoted() {
myFixture.configureByText("OTP-PUB-KEY.erl", "-module('OTP-PUB-KEY'). -export(['dec_D-1'/2]). 'dec_D-1'(Tlv, TagIn) -> 1.");
doCheckResult("foo() -> Odec<caret>", "foo() -> 'OTP-PUB-KEY':'dec_D-1'(<caret>)");
}
public void testModuleFunctionCompletionQuoted2() {
myFixture.configureByText("OTP-PUB-KEY.erl", "-module('OTP-PUB-KEY'). -export([dec_D1/2]). dec_D1(Tlv, TagIn) -> 1.");
doCheckResult("foo() -> Odec<caret>", "foo() -> 'OTP-PUB-KEY':dec_D1(<caret>)");
}
public void testModuleFunctionCompletionQuoted3() {
myFixture.configureByText("OTP-PUB-KEY.erl", "-module('OTP-PUB-KEY'). -export(['dec_D1'/2]). 'dec_D1'(Tlv, TagIn) -> 1.");
doCheckResult("foo() -> Odec<caret>", "foo() -> 'OTP-PUB-KEY':dec_D1(<caret>)");
}
public void testModuleFunctionCompletionQuoted4() {
myFixture.configureByText("otp_pub_key.erl", "-module(otp_pub_key). -export(['dec_D-1'/2]). 'dec_D-1'(Tlv, TagIn) -> 1.");
doCheckResult("foo() -> otpdec<caret>", "foo() -> otp_pub_key:'dec_D-1'(<caret>)");
}
public void testModuleCompletionWithColon() {
myFixture.configureByFiles("module-completion/test_module.erl");
doCheckResult("foo() -> test_modul<caret>", "foo() -> test_module:");
}
public void testModuleCompletionWithoutColon() {
myFixture.configureByFiles("module-completion/test_module.erl");
doCheckResult("foo() -> bar(test_modul<caret>", "foo() -> bar(test_module");
}
public void testBehaviourCompletion() {
myFixture.configureByFiles("module-completion/behaviour_module.erl");
doCheckResult("-behaviour(behaviour_mo<caret>).", "-behaviour(behaviour_module).");
}
public void testBehaviourInfoCompletion() {
myFixture.configureByFiles("module-completion/behaviour_info_module.erl");
doCheckResult("-behaviour(behaviour_in<caret>).", "-behaviour(behaviour_info_module).");
}
public void testQuotedBehaviourCompletion() {
myFixture.configureByFiles("module-completion/quoted-behaviour.erl");
doCheckResult("-behaviour(quo<caret>).", "-behaviour('quoted-behaviour').");
}
public void testBehaviourCompletionWithEndQuote() {
myFixture.configureByFiles("module-completion/quoted-behaviour.erl");
doCheckResult("-behaviour(quo<caret>').", "-behaviour('quoted-behaviour').");
}
public void testBehaviourCompletionWithBothQuotes() {
myFixture.configureByFiles("module-completion/quoted-behaviour.erl");
doCheckResult("-behaviour('quo<caret>').", "-behaviour('quoted-behaviour').");
}
public void test176() {
myFixture.configureByFiles("headers/a.erl", "headers/header.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "foo");
}
public void test465() {
myFixture.configureByFiles("465/a.erl", "465/specs.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "type1");
}
public void testFunctionExpression() {
doCheckResult("zoo() -> fun zo<caret>", "zoo() -> fun zoo/0");
}
public void testFunctionExpression2() {
doCheckResult("foo() -> fun <caret>", "foo() -> fun foo/0", Lookup.NORMAL_SELECT_CHAR);
}
public void test211() {
doTestInclude("-module(test, [Id, Name::string()]). foo() -> <caret>", "Id", "Name");
}
public void testNoCompletionInStrings() {
doTestVariants("foo() -> \"<caret>\"", CompletionType.BASIC, 1, CheckType.EQUALS);
}
public void testNoCompletionInComments() {
doTestVariants("% <caret>", CompletionType.BASIC, 1, CheckType.EQUALS);
}
public void _testIncludeCompletion() throws Exception {
localFileSystemSetUp();
myFixture.configureByFiles("include/includeCompletion.erl", "include/include/header.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.EQUALS, "include/");
}
public void testIncludeLibCompletion() {
myFixture.configureByFiles("include-lib/includeLib.erl", "include-lib/testapp/ebin/testapp.app", "include-lib/testapp/include/includefile.hrl");
myFixture.complete(CompletionType.BASIC);
myFixture.checkResultByFile("include-lib/includeLib-after.erl");
}
public void testIncludeLibEmptyCompletion() {
myFixture.configureByFiles("include-lib-empty/includeLib.erl",
"include-lib-empty/testapp/ebin/testapp.app",
"include-lib-empty/testapp/include/includefile.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "testapp/");
}
public void testSmartInteger() {
doSmartTest("-spec g(A :: integer()) -> integer().\n" +
"g(A) -> 1.\n" +
"foo() ->\n" +
" B = 2 / 1,\n" +
" B2 = \"\",\n" +
" B4 = (1),\n" +
" B3 = 1 + 1*1,\n" +
" g(<caret>);",
CheckType.EQUALS, "B4", "B3", "g");
}
public void testSmartCompositeTypes() {
doSmartTest(
"-spec new(Func::atom(), fun() | string()) -> integer().\n" +
"new(Func, StubFun) ->\n" +
" Str = \"\",\n" +
" Fun = fun () -> ok end,\n" +
" Fun2 = fun () -> ok end,\n" +
" new(atom, <caret>);\n" +
"new(Func, ClauseSpecs) -> ok.",
CheckType.EQUALS, "Fun", "Fun2" , "Str"
);
}
public void testCameCaseModules() {
myFixture.configureByText("CamelCase.erl", "");
myFixture.configureByText("a.erl", "bar() -> Cam<caret>");
myFixture.complete(CompletionType.BASIC, 2);
myFixture.checkResult("bar() -> 'CamelCase':<caret>");
}
public void testFunctionsFromCameCaseModule() {
myFixture.configureByText("CamelCase.erl", "-module('CamelCase'). -export([foo/0]). foo() -> ok.");
doTestInclude("bar() -> 'CamelCase':<caret>", "foo");
}
public void testOverrideInsideRecord() {
myFixture.configureByText("a.erl", "bar(Record, Record2) -> Rec<caret>#data{}.");
myFixture.completeBasic();
myFixture.type('\t');
myFixture.checkResult("bar(Record, Record2) -> Record#data{}.");
}
public void testFunctionTypeArguments() {
doTestInclude("-spec foo(Type) -> ok when <caret>", "Type");
}
public void testMacroArguments() {
doTestInclude("-define(M(Arg), <caret>", "Arg");
}
public void testFunctionImportsFromIncludes() {
myFixture.configureByFiles("imports/test.erl", "imports/funs.erl", "imports/importFuns.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "fun_a");
}
public void testFunctionImportsFromTransitiveIncludes() {
myFixture.configureByFiles("imports/testTransitive.erl", "imports/funs.erl",
"imports/importFuns.hrl", "imports/transitiveImportFuns.hrl");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "fun_a");
}
public void testVariableDeclarationInFunctionArguments() {
myFixture.configureByText("a.erl", "foo(FirstVar, First<caret>) -> ok.");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "FirstVar", "First");
}
public void testVariableDeclarationInLeftPartOfAssignment() {
myFixture.configureByText("a.erl", "foo(FirstVar) -> First<caret> = FirstVar.");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "First", "FirstVar");
}
public void testVariableDeclarationInPatternInLeftPartOfAssignment() {
myFixture.configureByText("a.erl", "foo(FirstVar) -> {ok, First<caret>} = {ok, FirstVar}.");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.INCLUDES, "First", "FirstVar");
}
public void testNoVariableDeclarationInRightPartOfAssignment() {
myFixture.configureByText("a.erl", "foo(FirstVar, FirstVar1) -> {ok, FirstVar} = {ok, First<caret>}.");
doTestVariantsInner(CompletionType.BASIC, 1, CheckType.EXCLUDES, "First");
}
}