/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.completion.handlers;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import gw.lang.parser.IToken;
import gw.lang.parser.statements.IClassDeclaration;
import gw.lang.parser.statements.IClassStatement;
import gw.lang.reflect.gs.GosuClassTypeLoader;
import gw.plugin.ij.completion.proposals.AdditionalSyntaxCompletionProposal;
import gw.plugin.ij.lang.psi.impl.GosuClassFileImpl;
import gw.plugin.ij.lang.psi.impl.statements.GosuFieldImpl;
import gw.plugin.ij.lang.psi.impl.statements.typedef.GosuClassDefinitionImpl;
import gw.plugin.ij.lang.psi.impl.statements.typedef.members.GosuMethodImpl;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Completes a symbol, not a .
*/
public class ClassKeywordsHandler extends AbstractCompletionHandler {
@NotNull
private final PrefixMatcher _prefixMatcher;
public ClassKeywordsHandler(CompletionParameters params, @NotNull CompletionResultSet result) {
super(params, result);
_prefixMatcher = result.getPrefixMatcher();
}
public void handleCompletePath() {
maybeAddKeywords();
}
private void maybeAddKeywords() {
for (AdditionalSyntaxCompletionProposal kw : getKeywordsThatMakeSense()) {
if (_prefixMatcher == null || kw.getGenericName().indexOf(_prefixMatcher.getPrefix()) == 0) {
kw.setWeight(-1);
addCompletion(kw);
}
}
}
public List<AdditionalSyntaxCompletionProposal> getKeywordsThatMakeSense() {
final PsiElement position = _params.getPosition();
if (position instanceof PsiComment) {
return Collections.emptyList();
}
final PsiElement parent = position.getParent();
if (parent instanceof GosuFieldImpl || parent instanceof GosuMethodImpl) {
return Collections.emptyList();
}
PsiElement grandParent = parent == null ? null : parent.getParent();
// TODO: use Keywords class from core Gosu
List<String> keywords = Lists.newArrayList(
"class", "interface", "structure", "enum",
"private", "public", "protected", "internal",
"abstract", "final");
// Enhancement file
if (position.getContainingFile().getName().endsWith(GosuClassTypeLoader.GOSU_ENHANCEMENT_FILE_EXT)) {
keywords.add("enhancement");
}
if (!(grandParent instanceof GosuClassFileImpl) &&
afterClassCurly(position, grandParent)) {
keywords.addAll(Arrays.asList("function", "property", "construct",
"var", "delegate", "override", "static"));
}
// Remove all the keywords already present
PsiElement previous = position.getPrevSibling();
List<String> previousKeywords = new ArrayList<>();
while (previous != null) {
previousKeywords.add(previous.getText());
previous = previous.getPrevSibling();
}
if (previousKeywords.contains("property")) {
keywords = Arrays.asList("get", "set");
}
if (previousKeywords.contains("function")) {
keywords = Collections.emptyList();
}
if (afterClassName(position, grandParent)) {
keywords = Arrays.asList("implements", "extends");
}
// Copy the list b/c it may not be mutable after all the asList() fixed-size stuff
keywords = new ArrayList<>(keywords);
keywords.removeAll(previousKeywords);
if (previousKeywords.contains("get") || previousKeywords.contains("set")) {
return Collections.emptyList();
}
ArrayList<AdditionalSyntaxCompletionProposal> keywordCompletionProposals = new ArrayList<>();
for (String keyword : keywords) {
if (keyword.equals("construct")) {
keywordCompletionProposals.add(new AdditionalSyntaxCompletionProposal(keyword, "()", -1));
} else {
keywordCompletionProposals.add(new AdditionalSyntaxCompletionProposal(keyword, " ", 0));
}
}
return keywordCompletionProposals;
}
private boolean afterClassName(@NotNull PsiElement position, @NotNull PsiElement grandParent) {
if (grandParent instanceof GosuClassDefinitionImpl) {
IClassStatement parsedElement = ((GosuClassDefinitionImpl) grandParent).getParsedElement();
IClassDeclaration classDeclaration = parsedElement.getClassDeclaration();
if (classDeclaration != null && classDeclaration.getLocation() != null &&
classDeclaration.getLocation().getOffset() < position.getTextOffset() &&
isBeforeFirstCurly(position.getTextOffset(), parsedElement.getTokens())) {
return true;
}
}
return false;
}
private boolean isBeforeFirstCurly(int textOffset, @NotNull List<IToken> tokens) {
for (IToken token : tokens) {
if ("{".equals(token.getText())) {
return textOffset <= token.getTokenStart();
}
}
return true;
}
private boolean afterClassCurly(@NotNull PsiElement position, @NotNull PsiElement grandParent) {
if (grandParent instanceof GosuClassDefinitionImpl) {
IClassStatement parsedElement = ((GosuClassDefinitionImpl) grandParent).getParsedElement();
return isAfterFirstCurly(position.getTextOffset(), parsedElement.getTokens());
}
return true;
}
private boolean isAfterFirstCurly(int textOffset, @NotNull List<IToken> tokens) {
for (IToken token : tokens) {
if ("{".equals(token.getText())) {
return textOffset >= token.getTokenStart();
}
}
return true;
}
}