/* * Copyright 2010 Ronnie Kolehmainen * * 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 com.github.cssxfire; import com.github.cssxfire.resolve.GotoDeclarationResolver; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.lang.css.CSSLanguage; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.css.*; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; /** * Created by IntelliJ IDEA. * User: Ronnie */ public class CssUtils { /** * See {@link #isDynamicCssLanguage(com.intellij.psi.PsiElement)} */ private static final Collection<FileType> DYNAMIC_CSS_FILETYPES = Arrays.asList( FileTypeManager.getInstance().getStdFileType("LESS"), FileTypeManager.getInstance().getStdFileType("SASS"), FileTypeManager.getInstance().getStdFileType("SCSS") ); public static CssDeclaration createDeclaration(Project project, String selector, String property, String value, boolean important) { CSSLanguage cssLanguage = Language.findInstance(CSSLanguage.class); String text = selector + " {" + property + ":" + value + (important ? " !important" : "") + ";}\n"; PsiFile dummyFile = PsiFileFactory.getInstance(project).createFileFromText("dummy.css", cssLanguage, text); return findFirstChildOfType(dummyFile, CssDeclaration.class); } public static CssRuleset createRuleset(Project project, String selector) { CSSLanguage cssLanguage = Language.findInstance(CSSLanguage.class); PsiFile dummyFile = PsiFileFactory.getInstance(project).createFileFromText("dummy.css", cssLanguage, selector + " {\n\n}\n"); return findFirstChildOfType(dummyFile, CssRuleset.class); } public static CssTerm createTerm(Project project, String value) { return findFirstChildOfType(createTermList(project, value), CssTerm.class); } public static CssTermList createTermList(Project project, String value) { CSSLanguage cssLanguage = Language.findInstance(CSSLanguage.class); PsiFile dummyFile = PsiFileFactory.getInstance(project).createFileFromText("dummy.css", cssLanguage, ".foo { color: " + value + " }"); return findFirstChildOfType(dummyFile, CssTermList.class); } private static <T extends PsiElement> T findFirstChildOfType(@NotNull PsiElement element, Class<T> type) { PsiElement[] children = element.getChildren(); for (PsiElement child : children) { if (type.isAssignableFrom(child.getClass())) { return (T) child; } T t = findFirstChildOfType(child, type); if (t != null) { return (T) t; } } return null; } @Nullable public static CssRulesetList findFirstCssRulesetList(@NotNull PsiFile file) { final Ref<CssRulesetList> ref = new Ref<CssRulesetList>(); PsiTreeUtil.processElements(file, new PsiElementProcessor() { public boolean execute(PsiElement element) { if (element instanceof CssRulesetList) { ref.set((CssRulesetList) element); return false; } return true; } }); return ref.get(); } public static PsiSearchHelper getPsiSearchHelper(Project project) { return ServiceManager.getService(project, PsiSearchHelper.class); } public static boolean processParents(@NotNull PsiElement element, @NotNull PsiElementProcessor<PsiElement> processor) { PsiElement parent = element.getParent(); while (parent != null) { if (parent instanceof PsiFile) { break; } if (!processor.execute(parent)) { return false; } parent = parent.getParent(); } return true; } @Nullable public static PsiElement resolveVariableAssignment(@NotNull CssDeclaration cssDeclaration) { CssTermList termList = PsiTreeUtil.getChildOfType(cssDeclaration, CssTermList.class); if (termList == null) { return null; } CssTerm[] terms = PsiTreeUtil.getChildrenOfType(termList, CssTerm.class); if (terms == null || terms.length != 1) { return null; // not an explicit variable reference } final Ref<PsiElement> resolved = new Ref<PsiElement>(null); PsiTreeUtil.processElements(terms[0], new PsiElementProcessor() { public boolean execute(@NotNull PsiElement element) { PsiElement[] targets = GotoDeclarationResolver.INSTANCE.getGotoDeclarationTargets(element, null); if (targets != null && targets.length == 1) { resolved.set(targets[0]); return false; } return true; } }); return resolved.get(); } public static boolean processCssDeclarations(@Nullable CssBlock block, final PsiElementProcessor<CssDeclaration> declarationProcessor) { if (block == null) { return false; } CssDeclaration[] declarations = PsiTreeUtil.getChildrenOfType(block, CssDeclaration.class); if (declarations != null) { for (CssDeclaration declaration : declarations) { if (!declarationProcessor.execute(declaration)) { return false; } } } if (isDynamicCssLanguage(block) && ProjectSettings.getInstance(block.getProject()).isResolveMixins()) { return PsiTreeUtil.processElements(block, new PsiElementProcessor() { public boolean execute(@NotNull PsiElement element) { PsiElement[] targets = GotoDeclarationResolver.INSTANCE.getGotoDeclarationTargets(element, null); if (targets != null && targets.length == 1) { PsiElement resolved = targets[0]; if (resolved instanceof CssRuleset) { if (!processCssDeclarations(((CssRuleset) resolved).getBlock(), declarationProcessor)) { return false; } } } return true; } }); } return true; } /** * Checks if the element is contained in a Less or Sass language file * * @param element an element * @return <tt>true</tt> if the element is a file or is contained in a file of type Less/Sass */ public static boolean isDynamicCssLanguage(@NotNull PsiElement element) { PsiFile file = element instanceof PsiFile ? (PsiFile) element : element.getContainingFile(); FileType fileType = file.getFileType(); return !(fileType instanceof PlainTextFileType) && DYNAMIC_CSS_FILETYPES.contains(fileType); } @Nullable public static CssMediumList findMediumList(@Nullable PsiElement element) { while (element != null) { ASTNode node = element.getNode(); if (node != null) { if ("CSS_MEDIA".equals(node.getElementType().toString())) { return PsiTreeUtil.getChildOfType(element, CssMediumList.class); } } element = element.getParent(); } return null; } }