/*
* Copyright 2013-2017 consulo.io
*
* 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 consulo.csharp.ide.completion;
import static com.intellij.patterns.StandardPatterns.or;
import static com.intellij.patterns.StandardPatterns.psiElement;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import consulo.csharp.ide.completion.patterns.CSharpPatterns;
import consulo.csharp.ide.completion.util.SpaceInsertHandler;
import consulo.csharp.lang.psi.*;
import consulo.csharp.lang.psi.CSharpTypeDeclaration;
import consulo.csharp.module.extension.CSharpLanguageVersion;
import consulo.csharp.module.extension.CSharpModuleUtil;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import consulo.annotations.RequiredReadAction;
import consulo.codeInsight.completion.CompletionProvider;
import consulo.dotnet.DotNetRunUtil;
import consulo.dotnet.psi.*;
/**
* @author VISTALL
* @since 07.01.14.
*/
public class CSharpKeywordCompletionContributor extends CompletionContributor
{
private static final Map<String, Boolean> ourPreprocessorDirectives = new HashMap<String, Boolean>();
static
{
ourPreprocessorDirectives.put("region", Boolean.TRUE);
ourPreprocessorDirectives.put("endregion", Boolean.FALSE);
ourPreprocessorDirectives.put("if", Boolean.TRUE);
ourPreprocessorDirectives.put("elif", Boolean.TRUE);
ourPreprocessorDirectives.put("else", Boolean.TRUE);
ourPreprocessorDirectives.put("endif", Boolean.FALSE);
}
public CSharpKeywordCompletionContributor()
{
extend(CompletionType.BASIC, psiElement(CSharpTokens.PREPROCESSOR_DIRECTIVE), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
PsiElement position = parameters.getPosition();
String trim = position.getText().replace(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED, "").trim();
if(!trim.equals("#"))
{
return;
}
for(Map.Entry<String, Boolean> entry : ourPreprocessorDirectives.entrySet())
{
LookupElementBuilder element = LookupElementBuilder.create(entry.getKey()).withPresentableText("#" + entry.getKey());
if(entry.getValue())
{
element = element.withInsertHandler(SpaceInsertHandler.INSTANCE);
}
element = element.bold();
result.addElement(element);
}
}
});
extend(CompletionType.BASIC, or(CSharpPatterns.field(), CSharpPatterns.statementStart()), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
for(Map.Entry<String, Boolean> entry : ourPreprocessorDirectives.entrySet())
{
LookupElementBuilder element = LookupElementBuilder.create("#" + entry.getKey()).withPresentableText("#" + entry.getKey());
if(entry.getValue())
{
element = element.withInsertHandler(SpaceInsertHandler.INSTANCE);
}
element = element.bold();
result.addElement(element);
}
}
});
extend(CompletionType.BASIC, CSharpPatterns.field(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
CSharpFieldDeclaration fieldDeclaration = PsiTreeUtil.getParentOfType(parameters.getPosition(), CSharpFieldDeclaration.class);
assert fieldDeclaration != null;
if(fieldDeclaration.isConstant() || fieldDeclaration.hasModifier(CSharpModifier.STATIC))
{
return;
}
CSharpCompletionUtil.elementToLookup(result, CSharpTokens.CONST_KEYWORD, CSharpCompletionUtil.ourSpaceInsert, null);
}
});
extend(CompletionType.BASIC, psiElement().afterLeaf(psiElement().withElementType(CSharpTokens.USING_KEYWORD)).inside(CSharpUsingNamespaceStatement.class), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
CSharpCompletionUtil.elementToLookup(result, CSharpTokens.STATIC_KEYWORD, null, new Condition<IElementType>()
{
@Override
@RequiredReadAction
public boolean value(IElementType elementType)
{
return CSharpModuleUtil.findLanguageVersion(parameters.getPosition()).isAtLeast(CSharpLanguageVersion._6_0);
}
});
}
});
extend(CompletionType.BASIC, psiElement().inside(DotNetGenericParameter.class), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
CSharpCompletionUtil.tokenSetToLookup(result, TokenSet.create(CSharpTokens.IN_KEYWORD, CSharpTokens.OUT_KEYWORD), null, new Condition<IElementType>()
{
@Override
@RequiredReadAction
public boolean value(IElementType elementType)
{
DotNetQualifiedElement qualifiedElement = PsiTreeUtil.getParentOfType(parameters.getPosition(), CSharpMethodDeclaration.class, DotNetTypeDeclaration.class);
if(qualifiedElement instanceof CSharpMethodDeclaration && ((CSharpMethodDeclaration) qualifiedElement).isDelegate())
{
return true;
}
if(qualifiedElement instanceof CSharpTypeDeclaration && ((CSharpTypeDeclaration) qualifiedElement).isInterface())
{
return true;
}
return false;
}
});
}
});
extend(CompletionType.BASIC, psiElement(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
PsiElement position = parameters.getPosition();
if(isCorrectPosition(position))
{
CSharpCompletionUtil.elementToLookup(result, CSharpSoftTokens.WHERE_KEYWORD, CSharpCompletionUtil.ourSpaceInsert, null);
}
}
@RequiredReadAction
private boolean isCorrectPosition(PsiElement position)
{
PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position);
if(prev == null)
{
return false;
}
if(PsiUtilCore.getElementType(prev) == CSharpTokens.RPAR)
{
DotNetGenericParameterListOwner listOwner = PsiTreeUtil.getParentOfType(prev, DotNetGenericParameterListOwner.class);
if(listOwner == null || listOwner.getGenericParametersCount() == 0)
{
return false;
}
return true;
}
return false;
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression().inside(CSharpUserType.class).inside(CSharpGenericConstraint.class).afterLeaf(psiElement(CSharpTokens.COLON)),
new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
TokenSet set = TokenSet.create(CSharpTokens.CLASS_KEYWORD, CSharpTokens.STRUCT_KEYWORD);
CSharpCompletionUtil.tokenSetToLookup(result, set, null, null);
}
});
extend(CompletionType.BASIC, psiElement(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet)
{
final PsiElement position = parameters.getPosition();
if(position.getParent() instanceof DotNetReferenceExpression && position.getParent().getParent() instanceof DotNetUserType)
{
PsiElement parent1 = position.getParent().getParent();
// dont allow inside statements
DotNetStatement statementParent = PsiTreeUtil.getParentOfType(parent1, DotNetStatement.class);
if(statementParent != null)
{
return;
}
PsiElement prevSibling = PsiTreeUtil.prevVisibleLeaf(parent1);
if(prevSibling == null ||
prevSibling.getNode().getElementType() == CSharpTokens.LBRACE ||
prevSibling.getNode().getElementType() == CSharpTokens.RBRACE ||
prevSibling.getNode().getElementType() == CSharpTokens.LPAR ||
prevSibling.getNode().getElementType() == CSharpTokens.COMMA ||
prevSibling.getNode().getElementType() == CSharpTokens.RBRACKET ||
prevSibling.getNode().getElementType() == CSharpTokens.SEMICOLON ||
prevSibling.getNode().getElementType() == CSharpTokens.PREPROCESSOR_DIRECTIVE ||
CSharpTokenSets.MODIFIERS.contains(prevSibling.getNode().getElementType()))
{
TokenSet tokenVal = TokenSet.orSet(CSharpTokenSets.MODIFIERS, CSharpTokenSets.TYPE_DECLARATION_START, TokenSet.create(CSharpTokens.DELEGATE_KEYWORD,
CSharpTokens.NAMESPACE_KEYWORD));
CSharpCompletionUtil.tokenSetToLookup(completionResultSet, tokenVal, CSharpCompletionUtil.ourSpaceInsert, new Condition<IElementType>()
{
@Override
@RequiredReadAction
public boolean value(IElementType elementType)
{
if(elementType == CSharpTokens.IN_KEYWORD)
{
return false;
}
if(elementType == CSharpSoftTokens.ASYNC_KEYWORD)
{
CSharpSimpleLikeMethodAsElement methodAsElement = PsiTreeUtil.getParentOfType(parameters.getOriginalPosition(), CSharpSimpleLikeMethodAsElement.class);
if(methodAsElement instanceof DotNetMethodDeclaration && DotNetRunUtil.isEntryPoint((DotNetMethodDeclaration) methodAsElement))
{
return false;
}
if(!CSharpModuleUtil.findLanguageVersion(position).isAtLeast(CSharpLanguageVersion._4_0))
{
return false;
}
}
DotNetParameter parameter = PsiTreeUtil.getParentOfType(position, DotNetParameter.class);
if(elementType == CSharpTokens.REF_KEYWORD ||
elementType == CSharpTokens.OUT_KEYWORD ||
elementType == CSharpTokens.THIS_KEYWORD ||
elementType == CSharpTokens.PARAMS_KEYWORD)
{
if(parameter == null)
{
return false;
}
if(elementType == CSharpTokenSets.THIS_KEYWORD)
{
return parameter.getIndex() == 0;
}
else if(elementType == CSharpTokens.PARAMS_KEYWORD)
{
DotNetParameterListOwner owner = parameter.getOwner();
return owner != null && ArrayUtil.getLastElement(owner.getParameters()) == parameter;
}
return true;
}
return parameter == null;
}
});
}
}
}
});
}
@RequiredReadAction
@Override
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result)
{
super.fillCompletionVariants(parameters, CSharpCompletionSorting.modifyResultSet(parameters, result));
}
}