下面列出了com.intellij.psi.util.PsiTreeUtil#getPrevSiblingOfType ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Gets the text of the continuous comments placed directly above the specified element
* @param element element whose previous siblings are enumerated and included if they're documentation comments
* @return the combined text of the documentation comments, preserving line breaks, or <code>null</code> if no documentation is available
*/
public static String getDocumentation(PsiElement element) {
final PsiComment comment = PsiTreeUtil.getPrevSiblingOfType(element, PsiComment.class);
final PsiElement previousElement =PsiTreeUtil.getPrevSiblingOfType(element, element.getClass());
if(isDocumentationComment(comment)) {
if(previousElement != null && previousElement.getTextOffset() > comment.getTextOffset()) {
// the comment is for another element of same type so no docs for this element
return null;
}
final List<PsiComment> siblings = Lists.newArrayList(comment);
getDocumentationCommentSiblings(comment, siblings, PsiElement::getPrevSibling);
Collections.reverse(siblings);
return siblings.stream().map(c -> StringUtils.stripStart(c.getText(), "# ")).collect(Collectors.joining("\n"));
}
return null;
}
/**
* Returns "@foo" value of ["@foo", "fo<caret>o"]
*/
@Nullable
public static String getPreviousSequenceItemAsText(@NotNull PsiElement psiElement) {
PsiElement yamlScalar = psiElement.getParent();
if(!(yamlScalar instanceof YAMLScalar)) {
return null;
}
PsiElement yamlSequence = yamlScalar.getParent();
if(!(yamlSequence instanceof YAMLSequenceItem)) {
return null;
}
// @TODO: catch new lexer error on empty item [<caret>,@foo] "PsiErrorElement:Sequence item expected"
YAMLSequenceItem prevSequenceItem = PsiTreeUtil.getPrevSiblingOfType(yamlSequence, YAMLSequenceItem.class);
if(prevSequenceItem == null) {
return null;
}
YAMLValue value = prevSequenceItem.getValue();
if(!(value instanceof YAMLScalar)) {
return null;
}
return ((YAMLScalar) value).getTextValue();
}
private static CompletionProvider<CompletionParameters> typeCompletionProvider() {
return new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(
@NotNull CompletionParameters completionParameters,
ProcessingContext processingContext,
@NotNull CompletionResultSet completionResultSet) {
PsiElement element = completionParameters.getPosition();
// Method parameter type in the Spec class
// PsiIdentifier -> PsiJavaCodeReferenceElement -> PsiTypeElement -> PsiMethod -> PsiClass
PsiElement typeElement = PsiTreeUtil.getParentOfType(element, PsiTypeElement.class);
if (typeElement == null) {
return;
}
PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (containingMethod == null) {
return;
}
PsiClass cls = containingMethod.getContainingClass();
if (!LithoPluginUtils.isLithoSpec(cls)) {
return;
}
// @Prop or @State annotation
PsiModifierList parameterModifiers =
PsiTreeUtil.getPrevSiblingOfType(typeElement, PsiModifierList.class);
if (parameterModifiers == null) {
return;
}
if (parameterModifiers.findAnnotation(Prop.class.getName()) != null) {
addCompletionResult(
completionResultSet, containingMethod, cls.getMethods(), LithoPluginUtils::isProp);
} else if (parameterModifiers.findAnnotation(State.class.getName()) != null) {
addCompletionResult(
completionResultSet, containingMethod, cls.getMethods(), LithoPluginUtils::isState);
}
}
};
}
public static boolean isJSGraphQLLanguageInjectionTarget(PsiElement host, @Nullable Ref<String> envRef) {
if (host instanceof JSStringTemplateExpression) {
JSStringTemplateExpression template = (JSStringTemplateExpression) host;
// check if we're a graphql tagged template
final JSReferenceExpression tagExpression = PsiTreeUtil.getPrevSiblingOfType(template, JSReferenceExpression.class);
if (tagExpression != null) {
final String tagText = tagExpression.getText();
if (SUPPORTED_TAG_NAMES.contains(tagText)) {
if (envRef != null) {
envRef.set(getEnvironmentFromTemplateTag(tagText, host));
}
return true;
}
final String builderTailName = tagExpression.getReferenceName();
if(builderTailName != null && SUPPORTED_TAG_NAMES.contains(builderTailName)) {
// a builder pattern that ends in a tagged template, e.g. someQueryAPI.graphql``
if(envRef != null) {
envRef.set(getEnvironmentFromTemplateTag(builderTailName, host));
}
return true;
}
}
// also check for "manual" language=GraphQL injection comments
final GraphQLCommentBasedInjectionHelper commentBasedInjectionHelper = ServiceManager.getService(GraphQLCommentBasedInjectionHelper.class);
if (commentBasedInjectionHelper != null) {
return commentBasedInjectionHelper.isGraphQLInjectedUsingComment(host, envRef);
}
}
return false;
}
/**
* Gets the PSI comment that starts the documentation for the specified element, or <code>null</code> if no documentation is available
*/
public static PsiComment getDocumentationStartElement(PsiElement element) {
final PsiComment comment = PsiTreeUtil.getPrevSiblingOfType(element, PsiComment.class);
if(isDocumentationComment(comment)) {
final List<PsiComment> siblings = Lists.newArrayList(comment);
getDocumentationCommentSiblings(comment, siblings, PsiElement::getPrevSibling);
Collections.reverse(siblings);
return siblings.get(0);
}
return null;
}
public static boolean isCommandParameterWord(PsiElement start) {
BashCommand command = PsiTreeUtil.getParentOfType(start, BashCommand.class);
if (command == null) {
return false;
}
BashWord word = PsiTreeUtil.getParentOfType(start, BashWord.class);
return word != null && PsiTreeUtil.getPrevSiblingOfType(word, BashGenericCommand.class) != null;
}
@Nullable
private static DotNetVariable getPrevVariable(@Nonnull CSharpStubVariableImpl<?> variable)
{
if(isMultipleDeclaration(variable))
{
CSharpVariableDeclStub<?> stub = variable.getStub();
if(stub != null)
{
StubElement<?> parentStub = stub.getParentStub();
PsiElement[] stubVariables = parentStub.getChildrenByType(variable.getElementType(), PsiElement.ARRAY_FACTORY);
int i = ArrayUtil.find(stubVariables, variable);
if(i <= 0)
{
LOGGER.error("Variable dont have type but dont second");
return null;
}
return (DotNetVariable) stubVariables[i - 1];
}
else
{
CSharpStubVariableImpl<?> prevVariable = PsiTreeUtil.getPrevSiblingOfType(variable, variable.getClass());
if(prevVariable == null)
{
LOGGER.error("Variable dont have type but dont second");
return null;
}
return prevVariable;
}
}
return null;
}
@Override
public void showParameterInfo(@NotNull PsiFunctionCallParams paramsOwner, @NotNull CreateParameterInfoContext context) {
PsiLowerSymbol functionName = PsiTreeUtil.getPrevSiblingOfType(paramsOwner, PsiLowerSymbol.class);
if (functionName != null) {
PsiReference reference = functionName.getReference();
PsiElement resolvedElement = reference == null ? null : reference.resolve();
if (resolvedElement instanceof PsiNamedElement) {
PsiElement resolvedParent = resolvedElement.getParent();
if (resolvedParent instanceof PsiLet) {
// If it's an alias, resolve to the alias
String alias = ((PsiLet) resolvedParent).getAlias();
if (alias != null) {
Project project = resolvedElement.getProject();
PsiFinder psiFinder = PsiFinder.getInstance(project);
PsiVal valFromAlias = psiFinder.findValFromQn(alias);
if (valFromAlias == null) {
PsiLet letFromAlias = psiFinder.findLetFromQn(alias);
if (letFromAlias != null) {
resolvedParent = letFromAlias;
}
} else {
resolvedParent = valFromAlias;
}
}
}
if (resolvedParent instanceof PsiSignatureElement) {
PsiSignature signature = ((PsiSignatureElement) resolvedParent).getPsiSignature();
if (signature != null) {
context.setItemsToShow(new Object[]{signature.asHMSignature()});
context.showHint(paramsOwner, paramsOwner.getTextOffset(), this);
} else if (resolvedParent instanceof PsiLet) {
PsiLet resolvedLet = (PsiLet) resolvedParent;
if (resolvedLet.isFunction()) {
// We don't have the real signature, we just display the function arguments
PsiFunction function = resolvedLet.getFunction();
if (function != null) {
Collection<PsiParameter> parameters = function.getParameters();
ORSignature hmSignature = new ORSignature(parameters);
context.setItemsToShow(new Object[]{hmSignature});
context.showHint(paramsOwner, paramsOwner.getTextOffset(), this);
}
}
}
}
}
}
}
private void completeFragmentOnTypeName() {
CompletionProvider<CompletionParameters> provider = new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
final PsiElement completionElement = parameters.getPosition();
// the type condition that the 'on' keyword belongs to
GraphQLTypeCondition typeCondition = PsiTreeUtil.getParentOfType(completionElement, GraphQLTypeCondition.class);
if (typeCondition == null) {
// typeCondition is on the left if the selection set follows
typeCondition = PsiTreeUtil.getPrevSiblingOfType(completionElement, GraphQLTypeCondition.class);
}
final boolean fragmentDefinition = typeCondition != null && typeCondition.getParent() instanceof GraphQLFragmentDefinition;
final GraphQLTypeDefinitionRegistryServiceImpl typeDefinitionRegistryService = GraphQLTypeDefinitionRegistryServiceImpl.getService(completionElement.getProject());
final TypeDefinitionRegistry typeDefinitionRegistry = typeDefinitionRegistryService.getRegistry(parameters.getOriginalFile());
final List<Pair<TypeDefinition, Description>> fragmentTypes = Lists.newArrayList();
if (fragmentDefinition) {
// completion in a top-level fragment definition, so add all known types, interfaces, unions
typeDefinitionRegistry.types().forEach((key, value) -> {
final boolean canFragment = value instanceof ObjectTypeDefinition || value instanceof UnionTypeDefinition || value instanceof InterfaceTypeDefinition;
if (canFragment) {
fragmentTypes.add(Pair.create(value, typeDefinitionRegistryService.getTypeDefinitionDescription(value)));
}
});
} else {
// inline fragment, so get type scope
GraphQLTypeScopeProvider typeScopeProvider = PsiTreeUtil.getParentOfType(completionElement, GraphQLTypeScopeProvider.class);
if (typeScopeProvider instanceof GraphQLInlineFragment && ((GraphQLInlineFragment) typeScopeProvider).getTypeCondition() == typeCondition) {
// if the type condition belongs to the type scope provider, we want the parent scope since that
// is the real source of what we can fragment on
typeScopeProvider = PsiTreeUtil.getParentOfType(typeScopeProvider, GraphQLTypeScopeProvider.class);
}
GraphQLType rawTypeScope = typeScopeProvider != null ? typeScopeProvider.getTypeScope() : null;
if (rawTypeScope != null) {
GraphQLUnmodifiedType typeScope = GraphQLUtil.getUnmodifiedType(rawTypeScope); // unwrap non-null and lists since fragments are about the raw type
final TypeDefinition fragmentType = typeDefinitionRegistry.getType(typeScope.getName()).orElse(null);
if (fragmentType != null) {
final Ref<Consumer<TypeDefinition<?>>> addTypesRecursive = new Ref<>();
final Consumer<TypeDefinition<?>> addTypes = (typeToFragmentOn) -> {
if (typeToFragmentOn instanceof ObjectTypeDefinition) {
fragmentTypes.add(Pair.create(typeToFragmentOn, typeDefinitionRegistryService.getTypeDefinitionDescription(typeToFragmentOn)));
final List<Type> anImplements = ((ObjectTypeDefinition) typeToFragmentOn).getImplements();
if (anImplements != null) {
anImplements.forEach(type -> {
final TypeDefinition typeDefinition = typeDefinitionRegistry.getType(type).orElse(null);
if (typeDefinition instanceof InterfaceTypeDefinition) {
fragmentTypes.add(Pair.create(typeDefinition, typeDefinitionRegistryService.getTypeDefinitionDescription(typeDefinition)));
}
});
}
} else if (typeToFragmentOn instanceof InterfaceTypeDefinition) {
fragmentTypes.add(Pair.create(typeToFragmentOn, typeDefinitionRegistryService.getTypeDefinitionDescription(typeToFragmentOn)));
final List<ObjectTypeDefinition> implementationsOf = typeDefinitionRegistry.getImplementationsOf((InterfaceTypeDefinition) typeToFragmentOn);
implementationsOf.forEach(impl -> fragmentTypes.add(Pair.create(impl, typeDefinitionRegistryService.getTypeDefinitionDescription(impl))));
} else if (typeToFragmentOn instanceof UnionTypeDefinition) {
final List<Type> memberTypes = ((UnionTypeDefinition) typeToFragmentOn).getMemberTypes();
if (memberTypes != null) {
memberTypes.forEach(memberType -> {
typeDefinitionRegistry.getType(memberType).ifPresent(memberTypeDefinition -> addTypesRecursive.get().consume(memberTypeDefinition));
});
}
}
};
addTypesRecursive.set(addTypes);
addTypes.consume(fragmentType);
}
}
}
fragmentTypes.forEach(fragmentType -> {
LookupElementBuilder element = LookupElementBuilder
.create(fragmentType.first.getName())
.withBoldness(true);
if (fragmentType.second != null) {
final String documentation = GraphQLDocumentationMarkdownRenderer.getDescriptionAsPlainText(fragmentType.second.getContent(), true);
element = element.withTailText(" - " + documentation, true);
}
result.addElement(element);
});
}
};
extend(CompletionType.BASIC, psiElement().afterLeaf(psiElement(GraphQLElementTypes.ON_KEYWORD)), provider);
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
final JSGraphQLEndpointNamedTypePsiElement unknownNamedType = getUnknownNamedType(element);
if (unknownNamedType != null) {
JSGraphQLEndpointNamedTypeDefinition definition = PsiTreeUtil.getParentOfType(element, JSGraphQLEndpointNamedTypeDefinition.class);
if (definition == null) {
// nearest type before cursor if not inside a type definition
definition = PsiTreeUtil.getPrevSiblingOfType(unknownNamedType, JSGraphQLEndpointNamedTypeDefinition.class);
}
if (definition != null) {
final IElementType type = getSupportedDefinitionType();
final String definitionText;
final boolean insertBefore = (type == JSGraphQLEndpointTokenTypes.INPUT);
Ref<Integer> caretOffsetAfterInsert = new Ref<>();
boolean indent = false;
if (type == JSGraphQLEndpointTokenTypes.UNION) {
definitionText = "\n\nunion " + unknownNamedType.getText() + " = \n";
caretOffsetAfterInsert.set(definitionText.length() - 1);
} else if (type == JSGraphQLEndpointTokenTypes.SCALAR) {
definitionText = "\n\nscalar " + unknownNamedType.getText() + "\n";
} else {
// all other types are <name> { ... }
final String beforeLines = insertBefore ? "" : "\n\n";
final String afterLines = insertBefore ? "\n\n" : "\n";
final int caretOffsetDelta = insertBefore ? 4 : 3; // we want the caret to be placed before closing '}' and the trailing newlines
definitionText = beforeLines + type.toString().toLowerCase() + " " + unknownNamedType.getText() + " {\n\n}" + afterLines;
caretOffsetAfterInsert.set(definitionText.length() - caretOffsetDelta);
indent = true;
}
final Document document = editor.getDocument();
final int insertOffset;
if(insertBefore) {
final PsiComment documentationStartElement = JSGraphQLEndpointDocPsiUtil.getDocumentationStartElement(definition);
if(documentationStartElement != null) {
insertOffset = documentationStartElement.getTextRange().getStartOffset();
} else {
insertOffset = definition.getTextRange().getStartOffset();
}
} else {
insertOffset = definition.getTextRange().getEndOffset();
}
document.insertString(insertOffset, definitionText);
if (caretOffsetAfterInsert.get() != null) {
// move caret to new position
PsiDocumentManager.getInstance(element.getProject()).commitDocument(document);
editor.getCaretModel().moveToOffset(insertOffset + caretOffsetAfterInsert.get());
if (indent) {
AnAction editorLineEnd = ActionManager.getInstance().getAction("EditorLineEnd");
if (editorLineEnd != null) {
final AnActionEvent actionEvent = AnActionEvent.createFromDataContext(
ActionPlaces.UNKNOWN,
null,
new DataManagerImpl.MyDataContext(editor.getComponent())
);
editorLineEnd.actionPerformed(actionEvent);
}
}
}
}
}
}
public JSGraphQLEndpointDocCompletionContributor() {
CompletionProvider<CompletionParameters> provider = new CompletionProvider<CompletionParameters>() {
@SuppressWarnings("unchecked")
@Override
protected void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
final PsiFile file = parameters.getOriginalFile();
if (!(file instanceof JSGraphQLEndpointDocFile)) {
return;
}
final PsiElement completionElement = Optional.ofNullable(parameters.getOriginalPosition()).orElse(parameters.getPosition());
if (completionElement != null) {
final PsiComment comment = PsiTreeUtil.getContextOfType(completionElement, PsiComment.class);
if (comment != null && JSGraphQLEndpointDocPsiUtil.isDocumentationComment(comment)) {
if (completionElement.getNode().getElementType() == JSGraphQLEndpointDocTokenTypes.DOCVALUE) {
final JSGraphQLEndpointFieldDefinition fieldDefinition = PsiTreeUtil.getNextSiblingOfType(comment, JSGraphQLEndpointFieldDefinition.class);
if (fieldDefinition != null && fieldDefinition.getArgumentsDefinition() != null) {
final List<String> otherDocTagValues = JSGraphQLEndpointDocPsiUtil.getOtherDocTagValues(comment);
for (JSGraphQLEndpointInputValueDefinition arg : PsiTreeUtil.findChildrenOfType(fieldDefinition.getArgumentsDefinition(), JSGraphQLEndpointInputValueDefinition.class)) {
final String argName = arg.getInputValueDefinitionIdentifier().getText();
if (!otherDocTagValues.contains(argName)) {
result.addElement(LookupElementBuilder.create(argName).withInsertHandler(AddSpaceInsertHandler.INSTANCE));
}
}
}
return;
}
final JSGraphQLEndpointDocTag tagBefore = PsiTreeUtil.getPrevSiblingOfType(completionElement, JSGraphQLEndpointDocTag.class);
final JSGraphQLEndpointDocTag tagParent = PsiTreeUtil.getParentOfType(completionElement, JSGraphQLEndpointDocTag.class);
if (tagBefore == null || tagParent != null) {
String completion = "param";
final boolean includeAt = completionElement.getNode().getElementType() != JSGraphQLEndpointDocTokenTypes.DOCNAME;
if (includeAt) {
completion = "@" + completion;
}
result.addElement(LookupElementBuilder.create(completion).withInsertHandler(AddSpaceInsertHandler.INSTANCE_WITH_AUTO_POPUP));
}
}
}
}
};
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), provider);
}
private boolean isBeforeQueryBody(PsiElement topmostElement) {
PsiElement previousQueryBody = PsiTreeUtil.getPrevSiblingOfType(topmostElement, XQueryQueryBody.class);
return previousQueryBody == null && (!(topmostElement instanceof PsiErrorElement) || !topmostElement.equals(getLastChild(topmostElement)));
}