/* * Copyright 2012-2014 Sergey Ignatov * * 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 org.intellij.erlang.rebar.util; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.intellij.erlang.ErlangFileType; import org.intellij.erlang.ErlangTypes; import org.intellij.erlang.psi.*; import org.intellij.erlang.psi.impl.ErlangElementFactory; import org.intellij.erlang.psi.impl.ErlangPsiImplUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.List; //TODO use traversers - no need to build PSI here public final class ErlangTermFileUtil { private ErlangTermFileUtil() { } @NotNull public static List<ErlangTupleExpression> findNamedTuples(@Nullable ErlangExpression listExpression) { return findNamedTuples(listExpression, null); } @NotNull private static List<ErlangTupleExpression> findNamedTuples(@Nullable ErlangExpression listExpression, @Nullable String name) { ErlangListExpression propList = listExpression instanceof ErlangListExpression ? (ErlangListExpression) listExpression : null; return propList != null ? findNamedTuples(propList.getExpressionList(), name) : ContainerUtil.<ErlangTupleExpression>emptyList(); } @NotNull private static List<ErlangTupleExpression> findNamedTuples(@NotNull List<ErlangExpression> configExpressions, @Nullable final String name) { return ContainerUtil.mapNotNull(configExpressions, expression -> { String tupleName = getNameOfNamedTuple(expression); boolean isValidName = name == null && tupleName != null || name != null && name.equals(tupleName); return isValidName ? (ErlangTupleExpression) expression : null; }); } @Nullable public static String getNameOfNamedTuple(@Nullable ErlangExpression expression) { ErlangTupleExpression tupleExpression = expression instanceof ErlangTupleExpression ? (ErlangTupleExpression) expression : null; List<ErlangExpression> expressions = tupleExpression != null ? tupleExpression.getExpressionList() : null; ErlangExpression configExpression = expressions != null && !expressions.isEmpty() ? expressions.get(0) : null; PsiElement nameQAtom = configExpression instanceof ErlangConfigExpression ? configExpression.getFirstChild() : null; ErlangAtom atom = nameQAtom instanceof ErlangQAtom ? ((ErlangQAtom) nameQAtom).getAtom() : null; return atom != null ? atom.getName() : null; } @Nullable public static ErlangFile createPsi(@NotNull VirtualFile file) { if (file.getFileType() != ErlangFileType.APP && file.getFileType() != ErlangFileType.TERMS) return null; try { String text = StringUtil.convertLineSeparators(VfsUtilCore.loadText(file)); Project defaultProject = ProjectManager.getInstance().getDefaultProject(); return (ErlangFile) PsiFileFactory.getInstance(defaultProject).createFileFromText(file.getName(), file.getFileType(), text); } catch (IOException e) { return null; } } public static void processConfigSection(@Nullable PsiElement configRoot, @NotNull String sectionName, @NotNull Consumer<ErlangExpression> sectionConsumer) { for (ErlangTupleExpression erlOptTuple : getConfigSections(configRoot, sectionName)) { List<ErlangExpression> expressions = erlOptTuple.getExpressionList(); ErlangExpression optionsList = expressions.size() >= 2 ? expressions.get(1) : null; if (optionsList == null) continue; sectionConsumer.consume(optionsList); } } @NotNull public static List<ErlangTupleExpression> getConfigSections(@Nullable PsiElement termsFile, @NotNull String sectionName) { return findNamedTuples(PsiTreeUtil.getChildrenOfTypeAsList(termsFile, ErlangExpression.class), sectionName); } @NotNull public static ErlangExpression createForm(String formText) { Project defaultProject = ProjectManager.getInstance().getDefaultProject(); PsiFile file = PsiFileFactory.getInstance(defaultProject).createFileFromText("a.config", ErlangFileType.TERMS, formText); ErlangExpression form = ContainerUtil.getFirstItem(PsiTreeUtil.getChildrenOfTypeAsList(file, ErlangExpression.class)); return ObjectUtils.assertNotNull(form); } public static void addListExpressionItem(@NotNull ErlangListExpression list, @NotNull ErlangExpression what) { boolean shouldAddComma = list.getChildren().length != 0; PsiElement leftBracket = list.getBracketLeft(); if (shouldAddComma) { PsiElement comma = ErlangElementFactory.createLeafFromText(ProjectManager.getInstance().getDefaultProject(), ","); list.addAfter(comma, leftBracket); } list.addAfter(what, leftBracket); } public static void deleteListExpressionItem(@NotNull PsiElement what) { ASTNode whatNode = what.getNode(); ASTNode commaAfterElement = TreeUtil.findSibling(whatNode, ErlangTypes.ERL_COMMA); ASTNode commaBeforeElement = TreeUtil.findSiblingBackward(whatNode, ErlangTypes.ERL_COMMA); PsiElement firstElementToDelete = commaBeforeElement != null && shouldDeleteComma(commaBeforeElement, whatNode) ? commaBeforeElement.getPsi() : what; PsiElement lastElementToDelete = commaAfterElement != null && shouldDeleteComma(commaAfterElement, whatNode) ? commaAfterElement.getPsi() : what; what.getParent().deleteChildRange(firstElementToDelete, lastElementToDelete); } private static boolean shouldDeleteComma(@NotNull ASTNode comma, @NotNull ASTNode listElementNode) { ASTNode leftNode = comma.getStartOffset() < listElementNode.getStartOffset() ? comma : listElementNode; ASTNode rightNode = comma.getStartOffset() < listElementNode.getStartOffset() ? listElementNode : comma; return elementsBetweenAreWhitespaceOrComment(leftNode, rightNode); } private static boolean elementsBetweenAreWhitespaceOrComment(@NotNull ASTNode first, @NotNull ASTNode last) { for (ASTNode node = first.getTreeNext(); node != null && node.getStartOffset() < last.getStartOffset(); node = node.getTreeNext()) { if (!ErlangPsiImplUtil.isWhitespaceOrComment(node)) { return false; } } return true; } }