/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2014-2014 AS3Boyan
* Copyright 2014-2014 Elias Ku
*
* 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.intellij.plugins.haxe.ide;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.parser.GeneratedParserUtilBase;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.patterns.StandardPatterns;
import com.intellij.plugins.haxe.HaxeLanguage;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.util.HaxeCodeGenerateUtil;
import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PlatformPatterns.psiElement;
/**
* @author: Fedor.Korotkov
*/
public class HaxeKeywordCompletionContributor extends CompletionContributor {
private static final Set<String> allowedKeywords = new THashSet<String>() {
{
for (IElementType elementType : HaxeTokenTypeSets.KEYWORDS.getTypes()) {
add(elementType.toString());
}
add(HaxeTokenTypes.ONEW.toString());
}
};
public HaxeKeywordCompletionContributor() {
final PsiElementPattern.Capture<PsiElement> idInExpression =
psiElement().withSuperParent(1, HaxeIdentifier.class).withSuperParent(2, HaxeReference.class);
final PsiElementPattern.Capture<PsiElement> inComplexExpression = psiElement().withSuperParent(3, HaxeReference.class);
final PsiElementPattern.Capture<PsiElement> inheritPattern =
psiElement().inFile(StandardPatterns.instanceOf(HaxeFile.class)).withSuperParent(1, PsiErrorElement.class).
and(psiElement().withSuperParent(2, HaxeInheritList.class));
extend(CompletionType.BASIC,
psiElement().andOr(psiElement().withSuperParent(1, PsiErrorElement.class),
psiElement().withSuperParent(1, GeneratedParserUtilBase.DummyBlock.class)).
andOr(psiElement().withSuperParent(2, HaxeClassBody.class), psiElement().withSuperParent(2, HaxeInheritList.class)),
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
result.addElement(LookupElementBuilder.create("extends"));
result.addElement(LookupElementBuilder.create("implements"));
}
});
// foo.b<caret> - bad
// i<caret> - good
extend(CompletionType.BASIC,
psiElement().inFile(StandardPatterns.instanceOf(HaxeFile.class)).andNot(idInExpression.and(inComplexExpression))
.andNot(inheritPattern),
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
final Collection<String> suggestedKeywords = suggestKeywords(parameters.getPosition());
suggestedKeywords.retainAll(allowedKeywords);
for (String keyword : suggestedKeywords) {
result.addElement(LookupElementBuilder.create(keyword));
}
}
});
}
private static Collection<String> suggestKeywords(PsiElement position) {
final TextRange posRange = position.getTextRange();
final HaxeFile posFile = (HaxeFile)position.getContainingFile();
final List<PsiElement> pathToBlockStatement = UsefulPsiTreeUtil.getPathToParentOfType(position, HaxeBlockStatement.class);
final HaxePsiCompositeElement classInterfaceEnum =
PsiTreeUtil.getParentOfType(position, HaxeClassBody.class, HaxeInterfaceBody.class, HaxeEnumBody.class);
final String text;
final int offset;
if (pathToBlockStatement != null) {
final Pair<String, Integer> pair = HaxeCodeGenerateUtil.wrapStatement(posRange.substring(posFile.getText()));
text = pair.getFirst();
offset = pair.getSecond();
}
else if (classInterfaceEnum != null) {
final Pair<String, Integer> pair = HaxeCodeGenerateUtil.wrapFunction(posRange.substring(posFile.getText()));
text = pair.getFirst();
offset = pair.getSecond();
}
else {
text = posFile.getText().substring(0, posRange.getStartOffset());
offset = 0;
}
final List<String> result = new ArrayList<String>();
if (pathToBlockStatement != null && pathToBlockStatement.size() > 1) {
final PsiElement blockChild = pathToBlockStatement.get(pathToBlockStatement.size() - 2);
result.addAll(suggestBySibling(UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(blockChild, true)));
}
PsiFile file = PsiFileFactory.getInstance(posFile.getProject()).createFileFromText("a.hx", HaxeLanguage.INSTANCE, text, true, false);
GeneratedParserUtilBase.CompletionState state = new GeneratedParserUtilBase.CompletionState(text.length() - offset);
file.putUserData(GeneratedParserUtilBase.COMPLETION_STATE_KEY, state);
TreeUtil.ensureParsed(file.getNode());
result.addAll(state.items);
// always
result.add(HaxeTokenTypes.PPIF.toString());
result.add(HaxeTokenTypes.PPELSE.toString());
result.add(HaxeTokenTypes.PPELSEIF.toString());
result.add(HaxeTokenTypes.PPERROR.toString());
return result;
}
@NotNull
private static Collection<? extends String> suggestBySibling(@Nullable PsiElement sibling) {
if (HaxeIfStatement.class.isInstance(sibling)) {
return Arrays.asList(HaxeTokenTypes.KELSE.toString());
}
else if (HaxeTryStatement.class.isInstance(sibling) || HaxeCatchStatement.class.isInstance(sibling)) {
return Arrays.asList(HaxeTokenTypes.KCATCH.toString());
}
return Collections.emptyList();
}
}