下面列出了怎么用com.intellij.psi.impl.source.tree.TreeUtil的API类实例代码及写法,或者点击链接到github查看源代码。
@Override
protected String convert(PsiElement element) {
if (isLookupFunction(element.getNode()) || isNonStarCount(element.getNode())) {
return element.getText().toLowerCase();
}
if (element.getNode().getElementType() == CypherTypes.FUNCTION_NAME
&& TreeUtil.findChildBackward(element.getNode(), CypherTypes.K_COUNT) == null) {
String text = element.getText();
if (text == null || text.isEmpty()) {
return null;
}
if (Strings.isUpperCase(text)) {
return text.toLowerCase();
}
char first = text.charAt(0);
return Character.toLowerCase(first) + (text.length() > 1 ? text.substring(1) : "");
}
return null;
}
@SuppressWarnings("unchecked")
private static void addRuleRefFoldingDescriptors(List<FoldingDescriptor> descriptors, PsiElement root) {
for (RuleSpecNode specNode : PsiTreeUtil.findChildrenOfType(root, RuleSpecNode.class)) {
GrammarElementRefNode refNode = PsiTreeUtil.findChildOfAnyType(specNode, GrammarElementRefNode.class);
if (refNode == null) continue;
PsiElement nextSibling = refNode.getNextSibling();
if (nextSibling == null) continue;
int startOffset = nextSibling.getTextOffset();
ASTNode backward = TreeUtil.findChildBackward(specNode.getNode(), SEMICOLON);
if (backward == null) continue;
int endOffset = backward.getTextRange().getEndOffset();
if (startOffset >= endOffset) continue;
descriptors.add(new FoldingDescriptor(specNode, new TextRange(startOffset, endOffset)));
}
}
@Nullable
private static PsiFile getContainingFileByTree(@Nonnull final PsiElement changeScope) {
// there could be pseudo physical trees (JSPX/JSP/etc.) which must not translate
// any changes to document and not to fire any PSI events
final PsiFile psiFile;
final ASTNode node = changeScope.getNode();
if (node == null) {
psiFile = changeScope.getContainingFile();
}
else {
final FileElement fileElement = TreeUtil.getFileElement((TreeElement)node);
// assert fileElement != null : "Can't find file element for node: " + node;
// Hack. the containing tree can be invalidated if updating supplementary trees like HTML in JSP.
if (fileElement == null) return null;
psiFile = (PsiFile)fileElement.getPsi();
}
return psiFile.getNode() != null ? psiFile : null;
}
private static void markToReformatBeforeOrInsertWhitespace(final ASTNode left, @Nonnull final ASTNode right) {
final Language leftLang = left != null ? PsiUtilCore.getNotAnyLanguage(left) : null;
final Language rightLang = PsiUtilCore.getNotAnyLanguage(right);
ASTNode generatedWhitespace = null;
if (leftLang != null && leftLang.isKindOf(rightLang)) {
generatedWhitespace = LanguageTokenSeparatorGenerators.INSTANCE.forLanguage(leftLang).generateWhitespaceBetweenTokens(left, right);
}
else if (rightLang.isKindOf(leftLang)) {
generatedWhitespace = LanguageTokenSeparatorGenerators.INSTANCE.forLanguage(rightLang).generateWhitespaceBetweenTokens(left, right);
}
if (generatedWhitespace != null) {
final TreeUtil.CommonParentState parentState = new TreeUtil.CommonParentState();
TreeUtil.prevLeaf((TreeElement)right, parentState);
parentState.nextLeafBranchStart.getTreeParent().addChild(generatedWhitespace, parentState.nextLeafBranchStart);
}
else {
markToReformatBefore(right, true);
}
}
private static int getMatchingLength(@Nonnull FileElement treeElement, @Nonnull CharSequence text, boolean fromStart) {
int patternIndex = fromStart ? 0 : text.length() - 1;
int finalPatternIndex = fromStart ? text.length() - 1 : 0;
int direction = fromStart ? 1 : -1;
ASTNode leaf = fromStart ? TreeUtil.findFirstLeaf(treeElement, false) : TreeUtil.findLastLeaf(treeElement, false);
int result = 0;
while (leaf != null && (fromStart ? patternIndex <= finalPatternIndex : patternIndex >= finalPatternIndex)) {
if (!(leaf instanceof ForeignLeafPsiElement)) {
CharSequence chars = leaf.getChars();
if (chars.length() > 0) {
int matchingLength = getLeafMatchingLength(chars, text, patternIndex, finalPatternIndex, direction);
result += matchingLength;
if (matchingLength != chars.length()) {
break;
}
patternIndex += fromStart ? matchingLength : -matchingLength;
}
}
leaf = fromStart ? TreeUtil.nextLeaf(leaf, false) : TreeUtil.prevLeaf(leaf, false);
}
return result;
}
@Override
protected String convert(PsiElement element) {
if (CypherTokenType.class.isAssignableFrom(element.getNode().getElementType().getClass())) {
CypherTokenType type = (CypherTokenType) element.getNode().getElementType();
if (type.getOriginalName().startsWith("K_")) {
if (TreeUtil.findParent(element.getNode(), TokenSet.create(
CypherTypes.VARIABLE,
CypherTypes.LABEL_NAME,
CypherTypes.REL_TYPE_NAME,
CypherTypes.PROPERTY_KEY_NAME,
CypherTypes.NAMESPACE,
CypherTypes.PARAMETER,
CypherTypes.PROCEDURE_NAME)) != null) {
return null;
}
if (SPECIAL_FUNCTIONS.containsKey(type)) {
return SPECIAL_FUNCTIONS.get(type);
}
if (TO_LOWER_CASE_SPECIAL.contains(type)) {
return element.getText().toLowerCase();
}
if (type == CypherTypes.K_NULL) {
// if no K_IS sibling to K_NULL -> to lower case
ASTNode keywordIS = TreeUtil.findSiblingBackward(element.getNode(), CypherTypes.K_IS);
if (keywordIS == null) {
return element.getText().toLowerCase();
}
}
return element.getText().toUpperCase();
}
}
return null;
}
/**
* Shorthand method for removing CRLF element.
*
* @param startElement working PSI element
*/
private void removeCrlf(PsiElement startElement) {
ASTNode node = TreeUtil.findSibling(startElement.getNode(), IgnoreTypes.CRLF);
if (node == null) {
node = TreeUtil.findSiblingBackward(startElement.getNode(), IgnoreTypes.CRLF);
}
if (node != null) {
node.getPsi().delete();
}
}
@Override
public void collect(@NotNull TwigFileVariableCollectorParameter parameter, @NotNull Map<String, Set<String>> variables) {
ASTNode macroStatement = TreeUtil.findParent(parameter.getElement().getNode(), TwigElementTypes.MACRO_STATEMENT);
if(macroStatement == null) {
return;
}
PsiElement psiElement = macroStatement.getPsi();
if(psiElement == null) {
return;
}
PsiElement marcoTag = psiElement.getFirstChild();
if(marcoTag == null) {
return;
}
Pair<String, String> pair = TwigUtil.getTwigMacroNameAndParameter(marcoTag);
if(pair == null || pair.getSecond() == null) {
return;
}
// strip braces "(foobar, foo)"
String args = StringUtils.stripStart(pair.getSecond(), "( ");
args = StringUtils.stripEnd(args, ") ");
for (String s : args.split("\\s*,\\s*")) {
variables.put(s, Collections.emptySet());
}
}
private Collection<String> suggestKeywords(@NotNull PsiElement position) {
PsiFile psiFile = position.getContainingFile();
PsiElement topLevelElement = position;
while (!(topLevelElement.getParent() instanceof PsiFile)) {
topLevelElement = topLevelElement.getParent();
}
PsiFile file = PsiFileFactory.getInstance(psiFile.getProject())
.createFileFromText("a.thrift", ThriftLanguage.INSTANCE, topLevelElement.getText(), true, false);
GeneratedParserUtilBase.CompletionState state =
new GeneratedParserUtilBase.CompletionState(position.getTextOffset() - topLevelElement.getTextOffset());
file.putUserData(GeneratedParserUtilBase.COMPLETION_STATE_KEY, state);
TreeUtil.ensureParsed(file.getNode());
return state.items;
}
private ReplaceElementWithEvents(@Nonnull CompositeElement oldRoot, @Nonnull CompositeElement newRoot) {
myOldRoot = oldRoot;
myNewRoot = newRoot;
// parse in background to reduce time spent in EDT and to ensure the newRoot light containing file is still valid
TreeUtil.ensureParsed(myOldRoot.getFirstChildNode());
TreeUtil.ensureParsed(myNewRoot.getFirstChildNode());
}
public static ASTNode addChildren(ASTNode parent, @Nonnull ASTNode first, @Nonnull ASTNode last, ASTNode anchorBefore) {
ASTNode lastChild = last.getTreeNext();
ASTNode current = first;
while (current != lastChild) {
saveWhitespacesInfo(current);
checkForOuters(current);
current = current.getTreeNext();
}
if (anchorBefore != null && CommentUtilCore.isComment(anchorBefore)) {
final ASTNode anchorPrev = anchorBefore.getTreePrev();
if (anchorPrev != null && anchorPrev.getElementType() == TokenType.WHITE_SPACE) {
anchorBefore = anchorPrev;
}
}
parent.addChildren(first, lastChild, anchorBefore);
ASTNode firstAddedLeaf = findFirstLeaf(first, last);
ASTNode prevLeaf = TreeUtil.prevLeaf(first);
ASTNode result = first;
if (firstAddedLeaf != null) {
ASTNode placeHolderEnd = makePlaceHolderBetweenTokens(prevLeaf, firstAddedLeaf, isFormattingRequired(prevLeaf, first), false);
if (placeHolderEnd != prevLeaf && first == firstAddedLeaf) {
result = placeHolderEnd;
}
ASTNode lastAddedLeaf = findLastLeaf(first, last);
placeHolderEnd = makePlaceHolderBetweenTokens(lastAddedLeaf, TreeUtil.nextLeaf(last), true, false);
if (placeHolderEnd != lastAddedLeaf && lastAddedLeaf == first) {
result = placeHolderEnd;
}
}
else {
makePlaceHolderBetweenTokens(prevLeaf, TreeUtil.nextLeaf(last), isFormattingRequired(prevLeaf, first), false);
}
return result;
}
public static void replaceChild(ASTNode parent, @Nonnull ASTNode oldChild, @Nonnull ASTNode newChild) {
saveWhitespacesInfo(oldChild);
saveWhitespacesInfo(newChild);
checkForOuters(oldChild);
checkForOuters(newChild);
LeafElement oldFirst = TreeUtil.findFirstLeaf(oldChild);
parent.replaceChild(oldChild, newChild);
final LeafElement firstLeaf = TreeUtil.findFirstLeaf(newChild);
final ASTNode prevToken = TreeUtil.prevLeaf(newChild);
if (firstLeaf != null) {
final ASTNode nextLeaf = TreeUtil.nextLeaf(newChild);
makePlaceHolderBetweenTokens(prevToken, firstLeaf, isFormattingRequired(prevToken, newChild), false);
if (nextLeaf != null && !CharArrayUtil.containLineBreaks(nextLeaf.getText())) {
makePlaceHolderBetweenTokens(TreeUtil.prevLeaf(nextLeaf), nextLeaf, false, false);
}
}
else {
if (oldFirst != null && prevToken == null) {
ASTNode whitespaceNode = newChild.getTreeNext();
if (whitespaceNode != null && whitespaceNode.getElementType() == TokenType.WHITE_SPACE) {
// Replacing non-empty prefix to empty shall remove whitespace
parent.removeChild(whitespaceNode);
}
}
makePlaceHolderBetweenTokens(prevToken, TreeUtil.nextLeaf(newChild), isFormattingRequired(prevToken, newChild), false);
}
}
@Nullable
private static ASTNode findFirstLeaf(ASTNode first, ASTNode last) {
do {
final LeafElement leaf = TreeUtil.findFirstLeaf(first);
if (leaf != null) return leaf;
first = first.getTreeNext();
if (first == null) return null;
}
while (first != last);
return null;
}
@Nullable
private static ASTNode findLastLeaf(ASTNode first, ASTNode last) {
do {
final ASTNode leaf = TreeUtil.findLastLeaf(last);
if (leaf != null) return leaf;
last = last.getTreePrev();
if (last == null) return null;
}
while (first != last);
return null;
}
@Nullable
public static PsiElement searchNonSpaceNonCommentBack(PsiElement element, final boolean strict) {
if(element == null || element.getNode() == null) return null;
ASTNode leftNeibour = TreeUtil.prevLeaf(element.getNode());
if (!strict) {
while (leftNeibour != null && (leftNeibour.getElementType() == TokenType.WHITE_SPACE || leftNeibour.getPsi() instanceof PsiComment)){
leftNeibour = TreeUtil.prevLeaf(leftNeibour);
}
}
return leftNeibour != null ? leftNeibour.getPsi() : null;
}
@NotNull
private Wrap calcWrap(@NotNull ASTNode node) {
IElementType type = node.getElementType();
if (type == CypherTypes.READING_CLAUSE || type == CypherTypes.UPDATING_CLAUSE || type == CypherTypes.READING_WITH_RETURN
|| type == CypherTypes.RETURN || type == CypherTypes.WITH) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.K_USING) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.CALL) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.MERGE_ACTION) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (isReturnBodyKeywords(node)) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.PATTERN_PART || type == CypherTypes.RELATIONSHIP_PATTERN) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
if (type == CypherTypes.WHERE && TreeUtil.findParent(node, CypherTypes.LIST_COMPREHENSION) != null) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
if (type == CypherTypes.WHERE) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.RETURN_ITEM) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
if (type == CypherTypes.FOREACH) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.K_ELSE || type == CypherTypes.K_END) {
return Wrap.createWrap(WrapType.ALWAYS, true);
}
if (type == CypherTypes.PROPERTY_KEY_NAME) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
if (node.getTreeParent().getElementType() == CypherTypes.ARRAY && type == CypherTypes.EXPRESSION) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
if (type == CypherTypes.K_AS) {
return Wrap.createWrap(WrapType.CHOP_DOWN_IF_LONG, true);
}
return Wrap.createWrap(WrapType.NONE, false);
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
if (!(file instanceof HaxeFile)) return null;
final List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
final ProblemReporter reporter = new ProblemReporter() {
@Override
public void reportProblem(ASTNode node, String message) {
result.add( manager.createProblemDescriptor( node.getPsi(),
message,
(LocalQuickFix)null,
ProblemHighlightType.ERROR,
isOnTheFly));
}
};
FileASTNode node1 = file.getNode();
LeafElement firstLeaf = TreeUtil.findFirstLeaf(node1);
Stack<List<ASTNode>> levels = new Stack<List<ASTNode>>();
List<ASTNode> nodes = new ArrayList<ASTNode>();
// Push the root node, just in case there is no #if to start it off.
levels.push(nodes);
ASTNode leaf = firstLeaf;
while (leaf != null) {
IElementType leafElementType = leaf.getElementType();
if (leafElementType.equals(HaxeTokenTypes.PPIF)) {
nodes = new ArrayList<ASTNode>();
levels.push(nodes);
nodes.add(leaf);
}
else if (leafElementType.equals(HaxeTokenTypes.PPEND)) {
nodes.add(leaf);
// Leave the base level in place, even if there are extra ends.
if (levels.size() > 1) {
validateLevel(nodes, reporter);
levels.pop();
nodes = levels.peek();
}
}
else if (leafElementType.equals(HaxeTokenTypes.PPELSEIF)) {
nodes.add(leaf);
}
else if (leafElementType.equals(HaxeTokenTypes.PPELSE)) {
nodes.add(leaf);
}
leaf = TreeUtil.nextLeaf(leaf);
}
// Any levels that are still left need to be validated.
for (List<ASTNode> level : levels) {
validateLevel(level, reporter);
}
return ArrayUtil.toObjectArray(result, ProblemDescriptor.class);
}
private boolean hasNoClosingTagName(ASTNode prevLeaf, XQueryXmlFullTag tag, XQueryXmlTagName tagName) {
return tag != null && StringUtil.isNotEmpty(tagName.getName()) && TreeUtil.findSibling(prevLeaf, XQueryTypes.XMLTAGNCNAME) == null;
}
protected ASTNode getPreviousNonWhiteSpaceLeaf(ASTNode originalPrevLeaf) {
ASTNode prevLeaf = originalPrevLeaf;
while ((prevLeaf = TreeUtil.prevLeaf(prevLeaf)) != null && prevLeaf.getElementType() == TokenType.WHITE_SPACE) {
}
return prevLeaf;
}
private void triggerParsingInFile(PsiFile file) {
TreeUtil.ensureParsed(file.getNode());
}
private static PsiElement getPsi(ASTNode node, PsiFile file) {
node.putUserData(TreeUtil.CONTAINING_FILE_KEY_AFTER_REPARSE, ((PsiFileImpl)file).getTreeElement());
PsiElement psiChild = file.isPhysical() ? node.getPsi() : null;
node.putUserData(TreeUtil.CONTAINING_FILE_KEY_AFTER_REPARSE, null);
return psiChild;
}
@Nullable
private static ASTNode makePlaceHolderBetweenTokens(ASTNode left, ASTNode right, boolean forceReformat, boolean normalizeTrailingWS) {
if (right == null) return left;
markToReformatBefore(right, false);
if (left == null) {
markToReformatBefore(right, true);
}
else if (left.getElementType() == TokenType.WHITE_SPACE && left.getTreeNext() == null && normalizeTrailingWS) {
// handle tailing whitespaces if element on the left has been removed
final ASTNode prevLeaf = TreeUtil.prevLeaf(left);
left.getTreeParent().removeChild(left);
markToReformatBeforeOrInsertWhitespace(prevLeaf, right);
left = right;
}
else if (left.getElementType() == TokenType.WHITE_SPACE && right.getElementType() == TokenType.WHITE_SPACE) {
final String text;
final int leftBlankLines = getBlankLines(left.getText());
final int rightBlankLines = getBlankLines(right.getText());
final boolean leaveRightText = leftBlankLines < rightBlankLines;
if (leftBlankLines == 0 && rightBlankLines == 0) {
text = left.getText() + right.getText();
}
else if (leaveRightText) {
text = right.getText();
}
else {
text = left.getText();
}
if (leaveRightText || forceReformat) {
final LeafElement merged = ASTFactory.whitespace(text);
if (!leaveRightText) {
left.getTreeParent().replaceChild(left, merged);
right.getTreeParent().removeChild(right);
}
else {
right.getTreeParent().replaceChild(right, merged);
left.getTreeParent().removeChild(left);
}
left = merged;
}
else {
right.getTreeParent().removeChild(right);
}
}
else if (left.getElementType() != TokenType.WHITE_SPACE || forceReformat) {
if (right.getElementType() == TokenType.WHITE_SPACE) {
markWhitespaceForReformat(right);
}
else if (left.getElementType() == TokenType.WHITE_SPACE) {
markWhitespaceForReformat(left);
}
else {
markToReformatBeforeOrInsertWhitespace(left, right);
}
}
return left;
}
@RequiredReadAction
@Override
public int getIndentInner(@Nonnull IndentHelper indentHelper, @Nonnull PsiFile file, @Nonnull ASTNode element, boolean includeNonSpace, int recursionLevel) {
if (recursionLevel > TOO_BIG_WALK_THRESHOLD) return 0;
if (element.getTreePrev() != null) {
ASTNode prev = element.getTreePrev();
ASTNode lastCompositePrev;
while (prev instanceof CompositeElement && !TreeUtil.isStrongWhitespaceHolder(prev.getElementType())) {
lastCompositePrev = prev;
prev = prev.getLastChildNode();
if (prev == null) { // element.prev is "empty composite"
return getIndentInner(indentHelper, file, lastCompositePrev, includeNonSpace, recursionLevel + 1);
}
}
String text = prev.getText();
int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r'));
if (index >= 0) {
return IndentHelperImpl.getIndent(file, text.substring(index + 1), includeNonSpace);
}
if (includeNonSpace) {
return getIndentInner(indentHelper, file, prev, includeNonSpace, recursionLevel + 1) + IndentHelperImpl.getIndent(file, text, includeNonSpace);
}
ASTNode parent = prev.getTreeParent();
ASTNode child = prev;
while (parent != null) {
if (child.getTreePrev() != null) break;
child = parent;
parent = parent.getTreeParent();
}
if (parent == null) {
return IndentHelperImpl.getIndent(file, text, includeNonSpace);
}
else {
return getIndentInner(indentHelper, file, prev, includeNonSpace, recursionLevel + 1);
}
}
else {
if (element.getTreeParent() == null) {
return 0;
}
return getIndentInner(indentHelper, file, element.getTreeParent(), includeNonSpace, recursionLevel + 1);
}
}