├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── README.md ├── intellij-common ├── .gitignore └── src │ └── main │ └── java │ └── com │ └── wix │ ├── ActualFile.java │ ├── ThreadLocalActualFile.java │ ├── annotator │ ├── AnnotatorUtils.java │ ├── BaseLintInspection.java │ ├── ExternalLintAnnotationInput.java │ ├── ExternalLintAnnotationResult.java │ └── LintExternalAnnotator.java │ ├── files │ ├── ActualFileManager.java │ ├── BaseActualFile.java │ ├── OriginalFile.java │ ├── RelativeFile.java │ └── TempFile.java │ ├── nodejs │ ├── CLI.java │ ├── NodeFinder.java │ └── NodeRunner.java │ ├── settings │ ├── ValidationInfo.java │ ├── ValidationUtils.java │ └── Validator.java │ └── utils │ ├── FileUtils.java │ ├── PsiUtil.java │ └── Strings.java └── scss-lint-plugin ├── .gitignore ├── LICENSE ├── build.gradle ├── docs ├── Inspection.png ├── PropertySortRule.png ├── Rule.png └── Settings.png ├── gradlew ├── gradlew.bat ├── lib └── xstream-1.4.7.jar ├── settings.gradle └── src ├── log4j-old.properties ├── log4j.xml ├── log4j0.xml ├── main ├── java │ └── com │ │ └── scss │ │ ├── EditSettingsAction.java │ │ ├── ScssLintBundle.java │ │ ├── ScssLintExternalAnnotator.java │ │ ├── ScssLintInspection.java │ │ ├── ScssLintProjectComponent.java │ │ ├── ThreadLocalActualFile.java │ │ ├── annotator │ │ ├── BaseActionFix.java │ │ ├── Fixes.java │ │ ├── PropertySortOrderFix.java │ │ └── SortProperty.kt │ │ ├── config │ │ ├── ScssLintConfigFileChangeTracker.java │ │ ├── ScssLintConfigFileType.java │ │ ├── ScssLintConfigFileTypeFactory.java │ │ └── ScssLintConfigFileUtil.java │ │ ├── settings │ │ ├── ScssLintSettingsPage.form │ │ ├── ScssLintSettingsPage.java │ │ └── Settings.java │ │ └── utils │ │ ├── ScssLintFinder.java │ │ ├── ScssLintRunner.java │ │ └── scssLint │ │ ├── Lint.java │ │ ├── LintResult.java │ │ └── Outdated.java └── resources │ ├── META-INF │ └── plugin.xml │ ├── com │ └── scss │ │ └── ScssLintBundle.properties │ └── inspectionDescriptions │ └── ScssLintInspection.html └── test ├── java ├── com │ └── scsslint │ │ ├── ScssLintTest.java │ │ ├── TestUtils.java │ │ ├── TestUtils2.java │ │ └── utils │ │ ├── ConfigFinderTest.java │ │ ├── PrintAppDataDir.java │ │ ├── ScssLintRunner2Test.java │ │ ├── ScssLintRunnerTest.java │ │ └── Settings.java └── log4j.properties └── resources ├── .scss-lint.yml ├── inspections ├── CapitalizationInSelector.scss ├── EmptyRule.scss ├── HexLength.scss └── one.scss ├── one.scss └── one.xml /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ### Versions 3 | 4 | scss-lint version: [?] 5 | You can get this information from executing `scss-lint --version` at the command line. 6 | 7 | OS: [?] 8 | IDEA product and version: [e.g. webstorm 11] 9 | 10 | ### Description 11 | 12 | [Description of the bug or feature] 13 | 14 | ### Steps to Reproduce 15 | 16 | 1. [First Step] 17 | 2. [Second Step] 18 | 3. [and so on...] 19 | 20 | **Expected behavior:** [What you expected to happen] 21 | 22 | **Actual behavior:** [What actually happened] 23 | 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must ends with two \r. 7 | Icon 8 | 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | .idea 18 | out 19 | 20 | /scss-lint-test 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SCSS Lint Plugin # 2 | 3 | [scss-lint](https://github.com/causes/scss-lint) is a tool to help keep your SCSS files clean and readable. see more [here](https://github.com/causes/scss-lint).
4 | SCSS Lint plugin for WebStorm, PHPStorm and other Idea family IDE with Javascript plugin, provides integration with SCSS Lint and shows errors and warnings inside the editor. 5 | * Support displaying SCSS Lint warnings as intellij inspections 6 | * Support quick fix for PropertySortRule that sorts the properties 7 | 8 | ## Getting started ## 9 | ### Prerequisites ### 10 | Install scss-lint on your machine see instructions [here](https://github.com/causes/scss-lint#installation):
11 | 12 | ### Settings ### 13 | To get started, you need to set the SCSS Lint plugin settings:
14 | 15 | * Go to preferences, SCSS Lint plugin page and check the Enable plugin. 16 | * Select the path to the SCSS Lint executable. (for windows users this might be your [rubypath]\bin\scss-lint.bat) 17 | * You can use the command `gem environment` to figure out where your gems are installed. Look for `GEM PATHS`. 18 | * Set the .scss-lint.yml file, or scss-lint will use the default settings. 19 | * By default, SCSS Lint plugin annotate the editor with warning or error based on the SCSS Lint configuration, you can check the 'Treat all SCSS Lint issues as warnings' checkbox to display all issues from SCSS Lint as warnings. 20 | 21 | 22 | Configuration:
23 | ![ESLint config](https://raw.githubusercontent.com/idok/scss-lint-plugin/master/scss-lint-plugin/docs/Settings.png) 24 | 25 | 26 | Inspection:
27 | ![ESLint inline](https://raw.githubusercontent.com/idok/scss-lint-plugin/master/scss-lint-plugin/docs/Rule.png) 28 | 29 | 30 | Analyze Code:
31 | ![ESLint inline](https://raw.githubusercontent.com/idok/scss-lint-plugin/master/scss-lint-plugin/docs/Inspection.png) 32 | -------------------------------------------------------------------------------- /intellij-common/.gitignore: -------------------------------------------------------------------------------- 1 | # /**/*.iml 2 | /target 3 | /.idea 4 | 5 | # Created by http://www.gitignore.io 6 | 7 | ### Intellij ### 8 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 9 | 10 | ## Directory-based project format 11 | .idea/ 12 | # if you remove the above rule, at least ignore user-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # and these sensitive or high-churn files: 16 | # .idea/dataSources.ids 17 | # .idea/dataSources.xml 18 | # .idea/sqlDataSources.xml 19 | # .idea/dynamic.xml 20 | 21 | ## File-based project format 22 | *.ipr 23 | *.iws 24 | *.iml 25 | 26 | ## Additional for IntelliJ 27 | out/ 28 | 29 | # generated by mpeltonen/sbt-idea plugin 30 | .idea_modules/ 31 | 32 | # generated by JIRA plugin 33 | atlassian-ide-plugin.xml 34 | 35 | # generated by Crashlytics plugin (for Android Studio and Intellij) 36 | com_crashlytics_export_strings.xml 37 | 38 | 39 | ### OSX ### 40 | .DS_Store 41 | .AppleDouble 42 | .LSOverride 43 | 44 | # Icon must ends with two \r. 45 | Icon 46 | 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # Files that might appear on external disk 52 | .Spotlight-V100 53 | .Trashes 54 | 55 | node_modules 56 | coverage 57 | target 58 | build 59 | npm-debug.log 60 | 61 | temp 62 | 63 | # gradle 64 | .gradle 65 | /build 66 | gradle.properties 67 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/ActualFile.java: -------------------------------------------------------------------------------- 1 | package com.wix; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import com.intellij.openapi.editor.Document; 5 | import com.intellij.openapi.fileEditor.FileDocumentManager; 6 | import com.intellij.openapi.util.Key; 7 | import com.intellij.openapi.util.io.FileUtil; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | /** 16 | * Process target file, either the real file or a temp file 17 | */ 18 | public class ActualFile { 19 | private static final Logger LOG = Logger.getInstance(Util.LOG_ID); 20 | 21 | ActualFile(File file, File tempFile) { 22 | this.file = file; 23 | this.tempFile = tempFile; 24 | } 25 | 26 | ActualFile(File file) { 27 | this(file, null); 28 | } 29 | 30 | private final File file; 31 | private final File tempFile; 32 | 33 | public File getFile() { 34 | return file; 35 | } 36 | 37 | public File getActualFile() { 38 | if (tempFile != null) { 39 | return tempFile; 40 | } 41 | return file; 42 | } 43 | 44 | public void deleteTemp() { 45 | if (tempFile != null && tempFile.exists() && tempFile.isFile()) { 46 | boolean isDeleted = tempFile.delete(); 47 | if (!isDeleted) { 48 | LOG.debug("Failed to delete temp file"); 49 | } 50 | } 51 | } 52 | 53 | @Nullable 54 | public static ActualFile getOrCreateActualFile(@NotNull Key key, @NotNull VirtualFile virtualFile, @Nullable String content) { 55 | FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); 56 | if (!fileDocumentManager.isFileModified(virtualFile)) { 57 | File file = new File(virtualFile.getPath()); 58 | if (file.isFile()) { 59 | return new ActualFile(file); 60 | } 61 | } 62 | ThreadLocalActualFile threadLocal = key.get(virtualFile); 63 | if (threadLocal == null) { 64 | threadLocal = virtualFile.putUserDataIfAbsent(key, new ThreadLocalActualFile(virtualFile)); 65 | } 66 | File file = threadLocal.getOrCreateFile(); 67 | if (file == null) { 68 | return null; 69 | } 70 | if (content == null) { 71 | Document document = fileDocumentManager.getDocument(virtualFile); 72 | if (document != null) { 73 | content = document.getText(); 74 | } 75 | } 76 | if (content == null) { 77 | return null; 78 | } 79 | try { 80 | FileUtil.writeToFile(file, content); 81 | return new ActualFile(new File(virtualFile.getPath()), file); 82 | } catch (IOException e) { 83 | LOG.warn("Can not write to " + file.getAbsolutePath(), e); 84 | } 85 | return null; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/ThreadLocalActualFile.java: -------------------------------------------------------------------------------- 1 | package com.wix; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import com.intellij.openapi.util.io.FileUtil; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.wix.utils.FileUtils; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Lint target file thread local storage 15 | */ 16 | public class ThreadLocalActualFile extends ThreadLocal { 17 | private final String baseName; 18 | private final String extension; 19 | private final VirtualFile originalFile; 20 | private File tempFile; 21 | private static final Logger LOG = Logger.getInstance(Util.LOG_ID); 22 | 23 | public boolean isTemp; 24 | 25 | private static final String SCSS_LINT_TMP = "_scsslint_tmp"; 26 | private static final String TEMP_DIR_NAME = "intellij-scsslint-temp"; 27 | 28 | public ThreadLocalActualFile(@NotNull VirtualFile originalFile) { 29 | this.baseName = originalFile.getNameWithoutExtension(); 30 | this.extension = FileUtils.getExtensionWithDot(originalFile); 31 | this.originalFile = originalFile; 32 | } 33 | 34 | public VirtualFile getFile() { 35 | return originalFile; 36 | } 37 | 38 | @Nullable 39 | public File getOrCreateFile() { 40 | String path = super.get(); 41 | if (path != null) { 42 | File file = new File(path); 43 | if (file.isFile()) { 44 | return file; 45 | } 46 | } 47 | File file = createFile(); 48 | if (file != null) { 49 | set(file.getAbsolutePath()); 50 | tempFile = file; 51 | return file; 52 | } 53 | return null; 54 | } 55 | 56 | @Nullable 57 | public static File getOrCreateTempDir() { 58 | File tmpDir = new File(FileUtil.getTempDirectory()); 59 | File dir = new File(tmpDir, TEMP_DIR_NAME); 60 | if (dir.isDirectory() || dir.mkdirs()) { 61 | return dir; 62 | } 63 | try { 64 | return FileUtil.createTempDirectory(tmpDir, TEMP_DIR_NAME, null); 65 | } catch (IOException ignored) { 66 | LOG.warn("Can't create '" + TEMP_DIR_NAME + "' temporary directory."); 67 | } 68 | return null; 69 | } 70 | 71 | private File createFileAsSibling() { 72 | File retFile; 73 | try { 74 | // try to create a temp file next to original file 75 | retFile = File.createTempFile(this.baseName + SCSS_LINT_TMP, this.extension, new File(originalFile.getParent().getPath())); 76 | isTemp = true; 77 | return retFile; 78 | } catch (IOException e) { 79 | LOG.warn("Can not create temp file", e); 80 | } 81 | return null; 82 | } 83 | 84 | @Nullable 85 | private File createFile() { 86 | // File retFile = new File(file.getParent().getPath(), file.getNameWithoutExtension() + "_jscs_tmp." + file.getExtension()); 87 | File retFile = createFileAsSibling(); 88 | if (retFile != null) { 89 | return retFile; 90 | } 91 | 92 | // try to create a temp file in temp folder 93 | File dir = getOrCreateTempDir(); 94 | if (dir == null) { 95 | return null; 96 | } 97 | File file = new File(dir, this.baseName + this.extension); 98 | boolean created = false; 99 | if (!file.exists()) { 100 | try { 101 | created = file.createNewFile(); 102 | } catch (IOException ignored) { 103 | LOG.warn("Can not create " + file.getAbsolutePath()); 104 | } 105 | } 106 | if (!created) { 107 | try { 108 | file = FileUtil.createTempFile(dir, this.baseName, this.extension); 109 | } catch (IOException e) { 110 | LOG.warn("Can not create temp file", e); 111 | return null; 112 | } 113 | } 114 | file.deleteOnExit(); 115 | return file; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/annotator/AnnotatorUtils.java: -------------------------------------------------------------------------------- 1 | package com.wix.annotator; 2 | 3 | import com.intellij.codeInsight.daemon.impl.HighlightInfoType; 4 | import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; 5 | import com.intellij.lang.annotation.HighlightSeverity; 6 | import com.intellij.openapi.editor.colors.EditorColorsManager; 7 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 8 | import com.intellij.openapi.editor.colors.TextAttributesKey; 9 | import com.intellij.openapi.editor.markup.TextAttributes; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public final class AnnotatorUtils { 14 | private AnnotatorUtils() { 15 | } 16 | 17 | @NotNull 18 | public static TextAttributes getTextAttributes(@Nullable EditorColorsScheme editorColorsScheme, @NotNull SeverityRegistrar severityRegistrar, @NotNull HighlightSeverity severity) { 19 | TextAttributes textAttributes = severityRegistrar.getTextAttributesBySeverity(severity); 20 | if (textAttributes != null) { 21 | return textAttributes; 22 | } 23 | EditorColorsScheme colorsScheme = getColorsScheme(editorColorsScheme); 24 | HighlightInfoType.HighlightInfoTypeImpl infoType = severityRegistrar.getHighlightInfoTypeBySeverity(severity); 25 | TextAttributesKey key = infoType.getAttributesKey(); 26 | return colorsScheme.getAttributes(key); 27 | } 28 | 29 | @NotNull 30 | private static EditorColorsScheme getColorsScheme(@Nullable EditorColorsScheme customScheme) { 31 | return customScheme == null ? EditorColorsManager.getInstance().getGlobalScheme() : customScheme; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/annotator/BaseLintInspection.java: -------------------------------------------------------------------------------- 1 | package com.wix.annotator; 2 | 3 | import com.intellij.codeInspection.*; 4 | import com.intellij.codeInspection.ex.UnfairLocalInspectionTool; 5 | import com.intellij.lang.annotation.ExternalAnnotator; 6 | import com.intellij.psi.PsiElementVisitor; 7 | import com.intellij.psi.PsiFile; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public abstract class BaseLintInspection extends LocalInspectionTool implements BatchSuppressableTool, UnfairLocalInspectionTool { //extends PropertySuppressableInspectionBase { 11 | 12 | protected abstract ExternalAnnotator getExternalAnnotator(); 13 | 14 | public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) { 15 | return ExternalAnnotatorInspectionVisitor.checkFileWithExternalAnnotator(file, manager, isOnTheFly, getExternalAnnotator()); 16 | } 17 | 18 | @NotNull 19 | @Override 20 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { 21 | return new ExternalAnnotatorInspectionVisitor(holder, getExternalAnnotator(), isOnTheFly); 22 | } 23 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/annotator/ExternalLintAnnotationInput.java: -------------------------------------------------------------------------------- 1 | package com.wix.annotator; 2 | 3 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiFile; 6 | 7 | public class ExternalLintAnnotationInput { 8 | public final String fileContent; 9 | public final EditorColorsScheme colorsScheme; 10 | public final Project project; 11 | public final PsiFile psiFile; 12 | 13 | public ExternalLintAnnotationInput(Project project, PsiFile psiFile, String fileContent, EditorColorsScheme colorsScheme) { 14 | this.project = project; 15 | this.psiFile = psiFile; 16 | this.fileContent = fileContent; 17 | this.colorsScheme = colorsScheme; 18 | } 19 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/annotator/ExternalLintAnnotationResult.java: -------------------------------------------------------------------------------- 1 | package com.wix.annotator; 2 | 3 | public class ExternalLintAnnotationResult { 4 | public ExternalLintAnnotationResult(ExternalLintAnnotationInput input, T result) { 5 | this.input = input; 6 | this.result = result; 7 | } 8 | 9 | public final ExternalLintAnnotationInput input; 10 | public final T result; 11 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/annotator/LintExternalAnnotator.java: -------------------------------------------------------------------------------- 1 | package com.wix.annotator; 2 | 3 | import com.intellij.lang.annotation.ExternalAnnotator; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * @author idok 11 | */ 12 | public abstract class LintExternalAnnotator extends ExternalAnnotator> { 13 | 14 | @Nullable 15 | @Override 16 | public ExternalLintAnnotationInput collectInformation(@NotNull PsiFile file) { 17 | return collectInformation(file, null); 18 | } 19 | 20 | @Nullable 21 | @Override 22 | public ExternalLintAnnotationInput collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { 23 | return collectInformation(file, editor); 24 | } 25 | 26 | protected abstract ExternalLintAnnotationInput collectInformation(PsiFile file, Editor editor); 27 | } 28 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/files/ActualFileManager.java: -------------------------------------------------------------------------------- 1 | package com.wix.files; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import com.intellij.openapi.editor.Document; 5 | import com.intellij.openapi.fileEditor.FileDocumentManager; 6 | import com.intellij.openapi.util.Key; 7 | import com.intellij.openapi.util.io.FileUtil; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.PsiFile; 10 | import com.wix.Util; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | public final class ActualFileManager { 18 | private static final Logger LOG = Logger.getInstance(Util.LOG_ID); 19 | 20 | private ActualFileManager() { 21 | } 22 | 23 | @Nullable 24 | public static BaseActualFile getOrCreateActualFile(@NotNull Key key, @NotNull PsiFile psiFile, @Nullable String content) { 25 | // Original file 26 | VirtualFile virtualFile = psiFile.getVirtualFile(); 27 | FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); 28 | if (!fileDocumentManager.isFileModified(virtualFile)) { 29 | File file = new File(virtualFile.getPath()); 30 | if (file.isFile()) { 31 | return new OriginalFile(psiFile, file); 32 | } 33 | } 34 | 35 | // TEMP File 36 | ThreadLocalTempActualFile threadLocal = key.get(virtualFile); 37 | if (threadLocal == null) { 38 | threadLocal = virtualFile.putUserDataIfAbsent(key, new ThreadLocalTempActualFile("scss-temp", psiFile)); 39 | } 40 | RelativeFile file = threadLocal.getOrCreateFile(); 41 | if (file == null) { 42 | return null; 43 | } 44 | if (content == null) { 45 | Document document = fileDocumentManager.getDocument(virtualFile); 46 | if (document != null) { 47 | content = document.getText(); 48 | } 49 | } 50 | if (content == null) { 51 | return null; 52 | } 53 | try { 54 | FileUtil.writeToFile(file.file, content); 55 | return new TempFile(psiFile, new File(virtualFile.getPath()), file); 56 | } catch (IOException e) { 57 | LOG.warn("Can not write to " + file.file.getAbsolutePath(), e); 58 | } 59 | return null; 60 | } 61 | 62 | public static void dispose(BaseActualFile actualCodeFile) { 63 | if (actualCodeFile != null) { 64 | actualCodeFile.deleteTemp(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/files/BaseActualFile.java: -------------------------------------------------------------------------------- 1 | package com.wix.files; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.wix.utils.FileUtils; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * Process target file, either the real file or a temp file 11 | */ 12 | public abstract class BaseActualFile { 13 | BaseActualFile(PsiFile psiFile, File file) { 14 | this.file = file; 15 | this.psiFile = psiFile; 16 | } 17 | 18 | protected final File file; 19 | private final PsiFile psiFile; 20 | 21 | public File getActualFile() { 22 | return file; 23 | } 24 | 25 | public void deleteTemp() { 26 | } 27 | 28 | @NotNull 29 | public String getPath() { 30 | return FileUtils.makeRelative(new File(psiFile.getProject().getBasePath()), file); 31 | } 32 | 33 | @NotNull 34 | public String getCwd() { 35 | return psiFile.getProject().getBasePath(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/files/OriginalFile.java: -------------------------------------------------------------------------------- 1 | package com.wix.files; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.io.File; 7 | 8 | public class OriginalFile extends BaseActualFile { 9 | OriginalFile(@NotNull PsiFile psiFile, File file) { 10 | super(psiFile, file); 11 | } 12 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/files/RelativeFile.java: -------------------------------------------------------------------------------- 1 | package com.wix.files; 2 | 3 | import java.io.File; 4 | 5 | public class RelativeFile { 6 | public final File root; 7 | public final File file; 8 | 9 | public RelativeFile(File root, File file) { 10 | this.root = root; 11 | this.file = file; 12 | } 13 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/files/TempFile.java: -------------------------------------------------------------------------------- 1 | package com.wix.files; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import com.intellij.psi.PsiFile; 5 | import com.wix.Util; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.io.File; 9 | 10 | public class TempFile extends BaseActualFile { 11 | private static final Logger LOG = Logger.getInstance(Util.LOG_ID); 12 | private final RelativeFile tempFile; 13 | 14 | TempFile(PsiFile psiFile, File file, RelativeFile tempFile) { 15 | super(psiFile, file); 16 | this.tempFile = tempFile; 17 | } 18 | 19 | @Override 20 | public File getActualFile() { 21 | return tempFile.file; 22 | } 23 | 24 | public RelativeFile getTempFile() { 25 | return tempFile; 26 | } 27 | 28 | @Override 29 | public void deleteTemp() { 30 | File temp = tempFile.file; 31 | if (temp != null && temp.exists() && temp.isFile()) { 32 | boolean isDeleted = temp.delete(); 33 | if (!isDeleted) { 34 | LOG.debug("Failed to delete temp file"); 35 | } 36 | } 37 | } 38 | 39 | @NotNull 40 | public String getPath() { 41 | return tempFile.file.getAbsolutePath(); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public String getCwd() { 47 | return tempFile.root.getAbsolutePath(); 48 | } 49 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/nodejs/CLI.java: -------------------------------------------------------------------------------- 1 | package com.wix.nodejs; 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine; 4 | import com.intellij.openapi.util.SystemInfo; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | 7 | public class CLI { 8 | public static final String JSON = "json"; 9 | public static final String FORMAT = "-f"; 10 | public static final String NO_COLOR = "--no-color"; 11 | public static final String VERSION = "--version"; 12 | public static final String V = "-v"; 13 | public static final String PARAM = "--"; 14 | public static final String ALIAS = "-"; 15 | 16 | private final GeneralCommandLine commandLine = new GeneralCommandLine(); 17 | 18 | public CLI(String cwd, String node, String exe) { 19 | if (SystemInfo.isWindows) { 20 | commandLine.setExePath(exe); 21 | } else { 22 | commandLine.setExePath(node); 23 | commandLine.addParameter(exe); 24 | } 25 | commandLine.setWorkDirectory(cwd); 26 | } 27 | 28 | public CLI noColor() { 29 | return param(NO_COLOR); 30 | } 31 | 32 | public CLI param(String value) { 33 | commandLine.addParameter(value); 34 | return this; 35 | } 36 | 37 | public CLI param(String key, String value) { 38 | commandLine.addParameter(key); 39 | commandLine.addParameter(value); 40 | return this; 41 | } 42 | 43 | public CLI json() { 44 | return param(FORMAT, JSON); 45 | } 46 | 47 | public GeneralCommandLine cmd() { 48 | return commandLine; 49 | } 50 | 51 | public static void addParam(GeneralCommandLine commandLine, String name, String value) { 52 | commandLine.addParameter(name); 53 | commandLine.addParameter(value); 54 | } 55 | 56 | public static void addParamIfNotEmpty(GeneralCommandLine commandLine, String name, String value) { 57 | if (StringUtil.isNotEmpty(value)) { 58 | addParam(commandLine, name, value); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/nodejs/NodeFinder.java: -------------------------------------------------------------------------------- 1 | package com.wix.nodejs; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.intellij.execution.configurations.PathEnvironmentVariableUtil; 5 | import com.intellij.openapi.util.SystemInfo; 6 | import com.intellij.openapi.util.text.StringUtil; 7 | import com.intellij.util.EnvironmentUtil; 8 | import com.intellij.util.containers.ContainerUtil; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.io.File; 13 | import java.util.Arrays; 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.Set; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | public final class NodeFinder { 21 | // public static final String RT_BASE_NAME = SystemInfo.isWindows ? "rt.cmd" : "rt"; 22 | private static final Pattern NVM_NODE_DIR_NAME_PATTERN = Pattern.compile("^v?(\\d+)\\.(\\d+)\\.(\\d+)$"); 23 | public static final String NODE_MODULES = "node_modules"; 24 | 25 | // TODO figure out a way to automatically get this path or add it to config 26 | // should read from /usr/local/lib/node_modules/eslint/lib/rules 27 | // public static String defaultPath = "/usr/local/lib/node_modules/eslint/lib/rules"; 28 | // c:/users/user/appdata/roaming/npm/node_modules 29 | 30 | private NodeFinder() { 31 | } 32 | 33 | public static String getBinName(String baseBinName) { 34 | return SystemInfo.isWindows ? baseBinName + ".cmd" : baseBinName; 35 | } 36 | 37 | // List infos = ContainerUtil.newArrayList(); 38 | // NodeModuleSearchUtil.findModulesWithName(infos, "eslint", project.getBaseDir(), null, false); 39 | 40 | // @Nullable 41 | // public static File findInterpreterInPath() { 42 | // return PathEnvironmentVariableUtil.findInPath(RT_BASE_NAME); 43 | // } 44 | 45 | @NotNull 46 | public static List searchNodeModulesBin(String exeFileName) { 47 | Set interpreters = ContainerUtil.newLinkedHashSet(); 48 | List fromPath = PathEnvironmentVariableUtil.findAllExeFilesInPath(exeFileName); 49 | List nvmInterpreters = listNodeInterpretersFromNvm(exeFileName); 50 | List brewInterpreters = listNodeInterpretersFromHomeBrew(exeFileName); 51 | interpreters.addAll(fromPath); 52 | interpreters.removeAll(nvmInterpreters); 53 | interpreters.removeAll(brewInterpreters); 54 | interpreters.addAll(nvmInterpreters); 55 | interpreters.addAll(brewInterpreters); 56 | return ContainerUtil.newArrayList(interpreters); 57 | } 58 | 59 | @NotNull 60 | public static List searchAllScopesForBin(File projectRoot, String exeFileName) { 61 | // List nodeModules = searchProjectNodeModules(projectRoot); 62 | List globalJscsBin = searchNodeModulesBin(exeFileName); 63 | File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName); 64 | if (file.exists()) { 65 | globalJscsBin.add(file); 66 | } 67 | 68 | // if (SystemInfo.isWindows) { 69 | // File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName); 70 | // if (file.exists()) { 71 | // globalJscsBin.add(file); 72 | // } 73 | // } else { 74 | // File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName); 75 | // if (file.exists()) { 76 | // globalJscsBin.add(file); 77 | // } 78 | // } 79 | // globalJscsBin.addAll(nodeModules); 80 | return globalJscsBin; 81 | } 82 | 83 | // @NotNull 84 | // public static List searchForRTBin(File projectRoot) { 85 | //// List nodeModules = searchProjectNodeModules(projectRoot); 86 | // List globalRTBin = listPossibleRTExe(exeFileName); 87 | 88 | // if (SystemInfo.isWindows) { 89 | // File file = resolvePath(projectRoot, NODE_MODULES, ".bin", "rt.cmd"); 90 | // if (file.exists()) { 91 | // globalRTBin.add(file); 92 | // } 93 | // } else { 94 | // File file = resolvePath(projectRoot, NODE_MODULES, "react-templates", "bin", "rt.js"); 95 | // if (file.exists()) { 96 | // globalRTBin.add(file); 97 | // } 98 | // } 99 | //// globalRTBin.addAll(nodeModules); 100 | // return globalRTBin; 101 | // } 102 | 103 | 104 | public static File resolvePath(File root, @Nullable String first, @Nullable String second, String... rest) { 105 | String path = buildPath(first, second, rest); 106 | return new File(root, path); 107 | } 108 | 109 | public static String buildPath(@Nullable String first, @Nullable String second, String... rest) { 110 | return Joiner.on(File.separatorChar).join(first, second, (Object[]) rest); 111 | } 112 | 113 | @NotNull 114 | public static List listNodeInterpretersFromNvm(String exeFileName) { 115 | String nvmDirPath = EnvironmentUtil.getValue("NVM_DIR"); 116 | if (StringUtil.isEmpty(nvmDirPath)) { 117 | return Collections.emptyList(); 118 | } 119 | File nvmDir = new File(nvmDirPath); 120 | if (nvmDir.isDirectory() && nvmDir.isAbsolute()) { 121 | return listNodeInterpretersFromVersionDir(nvmDir, exeFileName); 122 | } 123 | return Collections.emptyList(); 124 | } 125 | 126 | public static List listNodeInterpretersFromHomeBrew(String exeFileName) { 127 | return listNodeInterpretersFromVersionDir(new File("/usr/local/Cellar/node"), exeFileName); 128 | } 129 | 130 | public static List listNodeInterpretersFromVersionDir(@NotNull File parentDir, String exeFileName) { 131 | if (!parentDir.isDirectory()) { 132 | return Collections.emptyList(); 133 | } 134 | File[] dirs = parentDir.listFiles((dir, name) -> NodeFinder.structureNodeVersionStr(name) != null); 135 | if (dirs == null || dirs.length == 0) { 136 | return Collections.emptyList(); 137 | } 138 | Arrays.sort(dirs, (dir1, dir2) -> { 139 | int[] v1 = NodeFinder.structureNodeVersionStr(dir1.getName()); 140 | int[] v2 = NodeFinder.structureNodeVersionStr(dir2.getName()); 141 | if (v1 != null && v2 != null) { 142 | for (int i = 0; i < v1.length; i++) { 143 | if (i < v2.length) { 144 | int cmp = v2[i] - v1[i]; 145 | if (cmp != 0) { 146 | return cmp; 147 | } 148 | } 149 | } 150 | } 151 | return dir1.getName().compareTo(dir2.getName()); 152 | }); 153 | List interpreters = ContainerUtil.newArrayListWithCapacity(dirs.length); 154 | for (File dir : dirs) { 155 | File interpreter = new File(dir, "bin" + File.separator + exeFileName); 156 | if (interpreter.isFile() && interpreter.canExecute()) { 157 | interpreters.add(interpreter); 158 | } 159 | } 160 | return interpreters; 161 | } 162 | 163 | @Nullable 164 | private static int[] structureNodeVersionStr(@NotNull String name) { 165 | Matcher matcher = NVM_NODE_DIR_NAME_PATTERN.matcher(name); 166 | if (matcher.matches() && matcher.groupCount() == 3) { 167 | try { 168 | return new int[]{Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3))}; 169 | } catch (Exception ignored) { 170 | } 171 | } 172 | return null; 173 | } 174 | 175 | // public static File find(File projectRoot, String exe) { 176 | // if (SystemInfo.isWindows) { 177 | // File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, ".bin", exe); 178 | // if (file.exists()) { 179 | // return file; 180 | // } 181 | // } else { 182 | // File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, "grunt-packages", "bin", "packages.js"); 183 | // if (file.exists()) { 184 | // return file; 185 | // } 186 | // } 187 | // return null; 188 | // } 189 | 190 | /** 191 | * search for projectRoot/node_modules/.bin/exe 192 | * @param projectRoot node modules root 193 | * @param exe exe to find 194 | * @return file 195 | */ 196 | public static File findExeInProjectBin(File projectRoot, String exe) { 197 | File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, ".bin", exe); 198 | if (file.exists()) { 199 | return file; 200 | } 201 | return null; 202 | } 203 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/nodejs/NodeRunner.java: -------------------------------------------------------------------------------- 1 | package com.wix.nodejs; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.intellij.execution.ExecutionException; 5 | import com.intellij.execution.configurations.GeneralCommandLine; 6 | import com.intellij.execution.process.*; 7 | import com.intellij.openapi.diagnostic.Logger; 8 | import com.intellij.openapi.util.Key; 9 | import com.intellij.openapi.util.SystemInfo; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.io.File; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | public final class NodeRunner { 16 | private NodeRunner() { 17 | } 18 | 19 | private static final Logger LOG = Logger.getInstance(NodeRunner.class); 20 | 21 | private static final int TIME_OUT = (int) TimeUnit.SECONDS.toMillis(120L); 22 | 23 | /** 24 | * @param cwd working directory 25 | * @param node node interpreter path 26 | * @param exe node executable to run 27 | * @return command line to execute 28 | */ 29 | @NotNull 30 | public static GeneralCommandLine createCommandLine(@NotNull String cwd, String node, String exe) { 31 | GeneralCommandLine commandLine = new GeneralCommandLine(); 32 | if (!new File(cwd).exists() || !new File(exe).exists()) { 33 | throw new IllegalArgumentException("path doesn't exist"); 34 | } 35 | commandLine.setWorkDirectory(cwd); 36 | if (SystemInfo.isWindows) { 37 | commandLine.setExePath(exe); 38 | } else { 39 | if (!new File(node).exists()) { 40 | throw new IllegalArgumentException("path doesn't exist"); 41 | } 42 | commandLine.setExePath(node); 43 | commandLine.addParameter(exe); 44 | } 45 | return commandLine; 46 | } 47 | 48 | 49 | /** 50 | * @param commandLine command line to execute 51 | * @param timeoutInMilliseconds timeout 52 | * @return process output 53 | * @throws ExecutionException 54 | */ 55 | @NotNull 56 | public static ProcessOutput execute(@NotNull GeneralCommandLine commandLine, int timeoutInMilliseconds) throws ExecutionException { 57 | LOG.info("Running node command: " + commandLine.getCommandLineString()); 58 | Process process = commandLine.createProcess(); 59 | OSProcessHandler processHandler = new ColoredProcessHandler(process, commandLine.getCommandLineString(), Charsets.UTF_8); 60 | final ProcessOutput output = new ProcessOutput(); 61 | processHandler.addProcessListener(new ProcessAdapter() { 62 | public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { 63 | if (outputType.equals(ProcessOutputTypes.STDERR)) { 64 | output.appendStderr(event.getText()); 65 | } else if (!outputType.equals(ProcessOutputTypes.SYSTEM)) { 66 | output.appendStdout(event.getText()); 67 | } 68 | } 69 | }); 70 | processHandler.startNotify(); 71 | if (processHandler.waitFor(timeoutInMilliseconds)) { 72 | output.setExitCode(process.exitValue()); 73 | } else { 74 | processHandler.destroyProcess(); 75 | output.setTimeout(); 76 | } 77 | if (output.isTimeout()) { 78 | throw new ExecutionException("Command '" + commandLine.getCommandLineString() + "' is timed out."); 79 | } 80 | return output; 81 | } 82 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/settings/ValidationInfo.java: -------------------------------------------------------------------------------- 1 | package com.wix.settings; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.text.JTextComponent; 8 | 9 | public class ValidationInfo { 10 | public static final String LINK_TEMPLATE = "{{LINK}}"; 11 | private static final Logger LOG = Logger.getInstance(ValidationInfo.class); 12 | private final JTextComponent textComponent; 13 | private final String errorHtmlDescription; 14 | private final String linkText; 15 | 16 | public ValidationInfo(@Nullable JTextComponent textComponent, @NotNull String errorHtmlDescriptionTemplate, @NotNull String linkText) { 17 | this.textComponent = textComponent; 18 | if (!errorHtmlDescriptionTemplate.contains(LINK_TEMPLATE)) { 19 | LOG.warn("Cannot find {{LINK}} in " + errorHtmlDescriptionTemplate); 20 | } 21 | String linkHtml = "" + linkText + ""; 22 | this.errorHtmlDescription = errorHtmlDescriptionTemplate.replace(LINK_TEMPLATE, linkHtml); 23 | this.linkText = linkText; 24 | } 25 | 26 | @Nullable 27 | public JTextComponent getTextComponent() { 28 | return this.textComponent; 29 | } 30 | 31 | @NotNull 32 | public String getErrorHtmlDescription() { 33 | return this.errorHtmlDescription; 34 | } 35 | 36 | @Nullable 37 | public String getLinkText() { 38 | return this.linkText; 39 | } 40 | } -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/settings/ValidationUtils.java: -------------------------------------------------------------------------------- 1 | package com.wix.settings; 2 | 3 | import com.google.common.base.Strings; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | //import org.apache.commons.lang.StringUtils; 7 | 8 | import java.io.File; 9 | 10 | public final class ValidationUtils { 11 | 12 | private ValidationUtils() { 13 | } 14 | 15 | public static boolean validatePath(Project project, String path, boolean allowEmpty) { 16 | if (Strings.isNullOrEmpty(path)) { 17 | return allowEmpty; 18 | } 19 | File filePath = new File(path); 20 | if (filePath.isAbsolute()) { 21 | if (!filePath.exists() || !filePath.isFile()) { 22 | return false; 23 | } 24 | } else { 25 | if (project == null || project.getBaseDir() == null) { 26 | return true; 27 | } 28 | VirtualFile child = project.getBaseDir().findFileByRelativePath(path); 29 | if (child == null || !child.exists() || child.isDirectory()) { 30 | return false; 31 | } 32 | } 33 | return true; 34 | } 35 | 36 | public static boolean validateDirectory(Project project, String path, boolean allowEmpty) { 37 | if (Strings.isNullOrEmpty(path)) { 38 | return allowEmpty; 39 | } 40 | File filePath = new File(path); 41 | if (filePath.isAbsolute()) { 42 | if (!filePath.exists() || !filePath.isDirectory()) { 43 | return false; 44 | } 45 | } else { 46 | VirtualFile child = project.getBaseDir().findFileByRelativePath(path); 47 | if (child == null || !child.exists() || !child.isDirectory()) { 48 | return false; 49 | } 50 | } 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/settings/Validator.java: -------------------------------------------------------------------------------- 1 | package com.wix.settings; 2 | 3 | import javax.swing.*; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class Validator { 8 | 9 | private final List errs = new ArrayList<>(); 10 | 11 | public boolean hasErrors() { 12 | return !errs.isEmpty(); 13 | } 14 | 15 | public void add(JTextField textEditor, String s, String fixIt) { 16 | errs.add(new ValidationInfo(textEditor, s, fixIt)); 17 | } 18 | 19 | public List getErrors() { 20 | return errs; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.wix.utils; 2 | 3 | import com.google.common.base.Strings; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.io.FileUtil; 7 | import com.intellij.openapi.util.text.StringUtil; 8 | import com.intellij.openapi.vfs.VfsUtil; 9 | import com.intellij.openapi.vfs.VfsUtilCore; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import com.intellij.openapi.vfs.VirtualFileVisitor; 12 | import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; 13 | import com.intellij.openapi.vfs.newvfs.impl.VirtualFileImpl; 14 | import com.intellij.psi.PsiFile; 15 | import com.intellij.util.Function; 16 | import com.intellij.util.SmartList; 17 | import com.intellij.util.containers.ContainerUtil; 18 | //import com.scss.ScssLintBundle; 19 | import com.wix.Util; 20 | //import org.apache.commons.lang.StringUtils; 21 | import org.jetbrains.annotations.NotNull; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | import java.io.File; 25 | import java.io.FilenameFilter; 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.List; 29 | 30 | public final class FileUtils { 31 | private FileUtils() { 32 | } 33 | 34 | private static final Logger LOG = Logger.getInstance(Util.LOG_ID); 35 | 36 | // WildcardFileNameMatcher w = new WildcardFileNameMatcher("**/.jscsrc"); 37 | 38 | public static String makeRelative(VirtualFile root, VirtualFile absolutePath) { 39 | //FileUtil.getRelativePath(path, file.getPath().replace('/', File.separatorChar), File.separatorChar) 40 | return VfsUtil.getRelativePath(absolutePath, root, File.separatorChar); 41 | } 42 | 43 | public static String makeRelative(Project project, VirtualFile absolutePath) { 44 | //FileUtil.getRelativePath(path, file.getPath().replace('/', File.separatorChar), File.separatorChar) 45 | return makeRelative(project.getBaseDir(), absolutePath); 46 | } 47 | 48 | public static String relativePath(String root, String path) { 49 | return FileUtil.getRelativePath(new File(root), new File(path)); 50 | } 51 | 52 | public static String relativePath(Project project, VirtualFile absolutePath) { 53 | return FileUtil.getRelativePath(new File(project.getBasePath()), new File(absolutePath.getPath())); 54 | } 55 | 56 | public static String relativePath(PsiFile file) { 57 | return FileUtil.getRelativePath(new File(file.getProject().getBasePath()), new File(file.getVirtualFile().getPath())); 58 | } 59 | 60 | public static String getExtensionWithDot(VirtualFile file){ 61 | String ext = StringUtil.notNullize(file.getExtension()); 62 | if (!ext.startsWith(".")) { 63 | ext = '.' + ext; 64 | } 65 | return ext; 66 | } 67 | 68 | public static String makeRelative(File project, File absolutePath) { 69 | return FileUtil.getRelativePath(project, absolutePath); 70 | } 71 | 72 | public static boolean hasExtension(String file, String extension) { 73 | return file.endsWith(extension); 74 | } 75 | 76 | public static String removeExtension(String file) { 77 | int i = file.lastIndexOf('.'); 78 | return file.substring(0, i); 79 | } 80 | 81 | //file.substring(0, file.length() - ".js".length()) 82 | 83 | /** 84 | * resolve a relative or absolute path 85 | * @param project parent path 86 | * @param path path to file / folder 87 | * @return path 88 | */ 89 | public static String resolvePath(Project project, String path) { 90 | if (Strings.isNullOrEmpty(path)) { 91 | return null; 92 | } 93 | File filePath = new File(path); 94 | if (filePath.isAbsolute()) { 95 | if (!filePath.exists()) { 96 | return null; 97 | } 98 | return path; 99 | } else { 100 | if (project == null) { 101 | return null; 102 | } 103 | VirtualFile child = project.getBaseDir().findFileByRelativePath(path); 104 | if (child == null || !child.exists()) { 105 | return null; 106 | } 107 | return child.getPath(); 108 | } 109 | } 110 | 111 | @NotNull 112 | public static List displayDirectoryContents(@NotNull File projectRoot, @NotNull File dir, @NotNull FilenameFilter filter) { 113 | List ret = listFiles(projectRoot, dir, filter); 114 | File[] files = dir.listFiles(); 115 | for (final File file : files) { 116 | if (file.isDirectory()) { 117 | ret.addAll(displayDirectoryContents(projectRoot, file, filter)); 118 | // } else { 119 | // listFiles(file, filter, allFiles); 120 | } 121 | } 122 | return ret; 123 | } 124 | 125 | @NotNull 126 | public static List recursiveVisitor(@NotNull File dir, @NotNull FilenameFilter filter) { 127 | // String[] ret = dir.list(filter); 128 | List retList = new ArrayList<>(); 129 | // Collections.addAll(retList, ret); 130 | File[] files = dir.listFiles(); 131 | for (final File file : files) { 132 | if (file.isDirectory()) { 133 | retList.addAll(recursiveVisitor(file, filter)); 134 | } else { 135 | if (filter.accept(file.getParentFile(), file.getName())){ 136 | retList.add(file.getAbsolutePath()); 137 | } 138 | } 139 | } 140 | return retList; 141 | } 142 | 143 | public static List toAbsolutePath(List newFiles) { 144 | return ContainerUtil.map(newFiles, File::getAbsolutePath); 145 | } 146 | 147 | @NotNull 148 | public static List listFiles(@NotNull final File projectRoot, @NotNull final File dir, @NotNull FilenameFilter filter) { 149 | String[] curFiles = dir.list(filter); 150 | // allFiles.addAll(ret); //Arrays.asList(curFiles)); 151 | return ContainerUtil.map(curFiles, curFile -> { 152 | // TODO replace with makeRelative 153 | // return makeRelative(); 154 | return new File(dir, curFile).getAbsolutePath().substring(projectRoot.getAbsolutePath().length() + 1); 155 | }); 156 | } 157 | 158 | @NotNull 159 | private static List findExeFilesInPath(@Nullable String pathEnvVarValue, 160 | @NotNull String fileBaseName, 161 | boolean stopAfterFirstMatch, 162 | boolean logDetails) { 163 | if (logDetails) { 164 | LOG.info("Finding files in PATH (base name=" + fileBaseName + ", PATH=" + StringUtil.notNullize(pathEnvVarValue) + ")."); 165 | } 166 | if (pathEnvVarValue == null) { 167 | return Collections.emptyList(); 168 | } 169 | List result = new SmartList<>(); 170 | List paths = StringUtil.split(pathEnvVarValue, File.pathSeparator, true, true); 171 | for (String path : paths) { 172 | File dir = new File(path); 173 | if (logDetails) { 174 | File file = new File(dir, fileBaseName); 175 | LOG.info("path:" + path + ", path.isAbsolute:" + dir.isAbsolute() + ", path.isDirectory:" + dir.isDirectory() 176 | + ", file.isFile:" + file.isFile() + ", file.canExecute:" + file.canExecute()); 177 | } 178 | if (dir.isAbsolute() && dir.isDirectory()) { 179 | File file = new File(dir, fileBaseName); 180 | if (file.isFile() && file.canExecute()) { 181 | result.add(file); 182 | if (stopAfterFirstMatch) { 183 | return result; 184 | } 185 | } 186 | } 187 | } 188 | return result; 189 | } 190 | 191 | /** 192 | * 193 | * @param project containing project 194 | * @param path path to path to file / folder 195 | * @param allowEmpty allow empty path 196 | * @param isFile should be file or folder 197 | * @return validation status 198 | */ 199 | public static ValidationStatus validateProjectPath(Project project, String path, boolean allowEmpty, boolean isFile) { 200 | if (Strings.isNullOrEmpty(path)) { 201 | return allowEmpty ? ValidationStatus.VALID : ValidationStatus.IS_EMPTY; 202 | } 203 | File filePath = new File(path); 204 | if (filePath.isAbsolute()) { 205 | if (!filePath.exists()) { 206 | return ValidationStatus.DOES_NOT_EXIST; 207 | } 208 | if (isFile) { 209 | if (!filePath.isFile()) { 210 | return ValidationStatus.NOT_A_FILE; 211 | } 212 | } else { 213 | if (!filePath.isDirectory()) { 214 | return ValidationStatus.NOT_A_DIRECTORY; 215 | } 216 | } 217 | } else { 218 | if (project == null) { 219 | return ValidationStatus.DOES_NOT_EXIST; 220 | } 221 | VirtualFile child = project.getBaseDir().findFileByRelativePath(path); 222 | if (child == null || !child.exists()) { 223 | return ValidationStatus.DOES_NOT_EXIST; 224 | } 225 | if (isFile) { 226 | if (child.isDirectory()) { 227 | return ValidationStatus.NOT_A_FILE; 228 | } 229 | } else { 230 | if (!child.isDirectory()) { 231 | return ValidationStatus.NOT_A_DIRECTORY; 232 | } 233 | } 234 | } 235 | return ValidationStatus.VALID; 236 | } 237 | 238 | // public static class ValidationError { 239 | // public boolean a; 240 | // } 241 | // 242 | public enum ValidationStatus { 243 | VALID, IS_EMPTY, DOES_NOT_EXIST, NOT_A_DIRECTORY, NOT_A_FILE 244 | } 245 | 246 | public static boolean fileExists(String path) { 247 | if (Strings.isNullOrEmpty(path)) { 248 | return false; 249 | } 250 | File file = new File(path); 251 | return file.isFile() && file.exists(); 252 | } 253 | 254 | public static List getAllFilesInDirectory(VirtualFile directory, String target, String replacement) { 255 | List files = new ArrayList<>(); 256 | 257 | final VirtualFileVisitor fileVisitor = new VirtualFileVisitor(VirtualFileVisitor.SKIP_ROOT, VirtualFileVisitor.NO_FOLLOW_SYMLINKS) { 258 | @Override 259 | public boolean visitFile(@NotNull VirtualFile file) { 260 | // if (file instanceof VirtualDirectoryImpl) { 261 | // files.addAll(getAllFilesInDirectory(file, target, replacement)); 262 | // } else if (child instanceof VirtualFileImpl) { 263 | files.add(file.getPath().replace(target, replacement)); 264 | // } 265 | // if (ApplicationDictionary.SUPPORTED_APPLICATION_EXTENSIONS.contains(file.getExtension())) { 266 | // if (applicationName.equals(file.getNameWithoutExtension())) { 267 | // throw new MyStopVisitingException(file); 268 | // } 269 | // return false; //do not search inside application bundles 270 | // } 271 | // return true; 272 | } 273 | }; 274 | VfsUtilCore.visitChildrenRecursively(directory, fileVisitor) 275 | // VirtualFile[] children = directory.getChildren(); 276 | // for (VirtualFile child : children) { 277 | // if (child instanceof VirtualDirectoryImpl) { 278 | // files.addAll(getAllFilesInDirectory(child, target, replacement)); 279 | // } else if (child instanceof VirtualFileImpl) { 280 | // files.add(child.getPath().replace(target, replacement)); 281 | // } 282 | // } 283 | return files; 284 | } 285 | 286 | public static VirtualFile findFileByPath(VirtualFile path, String valuePath) { 287 | VirtualFile file = path.findFileByRelativePath(valuePath); 288 | if (null == file || file.isDirectory()) { 289 | file = path.findFileByRelativePath(valuePath + ".js"); 290 | } 291 | return file; 292 | } 293 | 294 | 295 | public static String join(String file, String ext) { 296 | return file + '/' + ext; 297 | } 298 | 299 | public static String removeExt(String file, String ext) { 300 | if (file.endsWith(ext)) { 301 | return file.replace(ext, ""); 302 | } 303 | return file; 304 | } 305 | 306 | public static String relativePath(VirtualFile root, VirtualFile file) { 307 | // get project relative path 308 | return file.getPath().substring(root.getPath().length() + 1); 309 | } 310 | 311 | public static String getNormalizedPath(int doubleDotCount, String[] pathsOfPath) { 312 | StringBuilder newValuePath = new StringBuilder(); 313 | for (int i = 0; i < pathsOfPath.length - doubleDotCount; i++) { 314 | if (0 != i) { 315 | newValuePath.append('/'); 316 | } 317 | newValuePath.append(pathsOfPath[i]); 318 | } 319 | return newValuePath.toString(); 320 | } 321 | 322 | public static int getDoubleDotCount(String valuePath) { 323 | int doubleDotCount = (valuePath.length() - valuePath.replaceAll("\\.\\.", "").length()) / 2; 324 | boolean doubleDotCountTrues = false; 325 | 326 | while (!doubleDotCountTrues && 0 != doubleDotCount) { 327 | if (valuePath.startsWith(StringUtil.repeat("../", doubleDotCount)) || valuePath.startsWith(StringUtil.repeat("../", doubleDotCount - 1) + "..")) { 328 | doubleDotCountTrues = true; 329 | } else { 330 | doubleDotCount--; 331 | } 332 | } 333 | return doubleDotCount; 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/utils/PsiUtil.java: -------------------------------------------------------------------------------- 1 | package com.wix.utils; 2 | 3 | import com.intellij.openapi.editor.Document; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | /** 9 | * Psi Utils 10 | * Created by idok on 6/26/14. 11 | */ 12 | public final class PsiUtil { 13 | private PsiUtil() { 14 | } 15 | 16 | /** 17 | * copied from com.intellij.psi.util.PsiUtilCore to fix compatibility issue with webstorm 18 | * @param file psi file 19 | * @param offset offset to search 20 | * @return psi element in the offset 21 | */ 22 | @NotNull 23 | public static PsiElement getElementAtOffset(@NotNull PsiFile file, int offset) { 24 | PsiElement elt = file.findElementAt(offset); 25 | if (elt == null && offset > 0) { 26 | elt = file.findElementAt(offset - 1); 27 | } 28 | if (elt == null) { 29 | return file; 30 | } 31 | return elt; 32 | } 33 | 34 | public static int calcErrorStartOffsetInDocument(@NotNull Document document, int lineStartOffset, int lineEndOffset, int column, int tabSize) { 35 | if (tabSize <= 1) { 36 | if (column < 0) { 37 | return lineStartOffset; 38 | } 39 | return Math.min(lineStartOffset + column, lineEndOffset); 40 | } 41 | CharSequence docText = document.getCharsSequence(); 42 | int offset = lineStartOffset; 43 | int col = 0; 44 | while (offset < lineEndOffset && col < column) { 45 | col += docText.charAt(offset) == '\t' ? tabSize : 1; 46 | offset++; 47 | } 48 | return offset; 49 | } 50 | 51 | public static int positionToOffset(@NotNull Document document, int line, int column, int tabSize) { 52 | int errorLine = line - 1; 53 | int errorColumn = column /*- 1*/; 54 | 55 | if (errorLine < 0 || errorLine >= document.getLineCount()) { 56 | return -1; 57 | } 58 | int lineEndOffset = document.getLineEndOffset(errorLine); 59 | int lineStartOffset = document.getLineStartOffset(errorLine); 60 | 61 | return calcErrorStartOffsetInDocument(document, lineStartOffset, lineEndOffset, errorColumn, tabSize); 62 | } 63 | 64 | // @Nullable 65 | // private static TextRange findRangeInDocument(@NotNull PsiFile file, @NotNull Document document, int line, int column, 66 | // int tabSize, boolean showErrorOnWholeLine) { 67 | // if (line < 0 || line >= document.getLineCount()) { 68 | // return null; 69 | // } 70 | // int lineEndOffset = document.getLineEndOffset(line); 71 | // int lineStartOffset = document.getLineStartOffset(line); 72 | // 73 | // int errorLineStartOffset = calcErrorStartOffsetInDocument(document, lineStartOffset, lineEndOffset, column, tabSize); 74 | // 75 | // if (errorLineStartOffset == -1) { 76 | // return null; 77 | // } 78 | // PsiElement element = file.findElementAt(errorLineStartOffset); 79 | //// if (element != null /*&& JSInspection.isSuppressedForStatic(element, getInspectionClass(), inspectionKey.getID())*/) 80 | //// return null; 81 | // TextRange range; 82 | // if (showErrorOnWholeLine) { 83 | // range = new TextRange(lineStartOffset, lineEndOffset); 84 | // } else { 85 | //// int offset = StringUtil.lineColToOffset(document.getText(), warn.line - 1, warn.column); 86 | // PsiElement lit = PsiUtil.getElementAtOffset(file, errorLineStartOffset); 87 | // range = lit.getTextRange(); 88 | //// range = new TextRange(errorLineStartOffset, errorLineStartOffset + 1); 89 | // } 90 | // return new TextRange(errorLineStartOffset, errorLineStartOffset + issue.length); 91 | // } 92 | } 93 | -------------------------------------------------------------------------------- /intellij-common/src/main/java/com/wix/utils/Strings.java: -------------------------------------------------------------------------------- 1 | package com.wix.utils; 2 | 3 | public final class Strings { 4 | private Strings() { 5 | } 6 | 7 | public static boolean areEqual(String a, String b) { 8 | if (a == null) { 9 | return b == null; 10 | } 11 | return a.equals(b); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scss-lint-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # /**/*.iml 2 | /target 3 | /.idea 4 | 5 | # Created by http://www.gitignore.io 6 | 7 | ### Intellij ### 8 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 9 | 10 | ## Directory-based project format 11 | .idea/ 12 | # if you remove the above rule, at least ignore user-specific stuff: 13 | # .idea/workspace.xml 14 | # .idea/tasks.xml 15 | # and these sensitive or high-churn files: 16 | # .idea/dataSources.ids 17 | # .idea/dataSources.xml 18 | # .idea/sqlDataSources.xml 19 | # .idea/dynamic.xml 20 | 21 | ## File-based project format 22 | *.ipr 23 | *.iws 24 | *.iml 25 | 26 | ## Additional for IntelliJ 27 | out/ 28 | 29 | # generated by mpeltonen/sbt-idea plugin 30 | .idea_modules/ 31 | 32 | # generated by JIRA plugin 33 | atlassian-ide-plugin.xml 34 | 35 | # generated by Crashlytics plugin (for Android Studio and Intellij) 36 | com_crashlytics_export_strings.xml 37 | 38 | 39 | ### OSX ### 40 | .DS_Store 41 | .AppleDouble 42 | .LSOverride 43 | 44 | # Icon must ends with two \r. 45 | Icon 46 | 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # Files that might appear on external disk 52 | .Spotlight-V100 53 | .Trashes 54 | 55 | # gradle 56 | .gradle 57 | /build 58 | gradle.properties 59 | 60 | node_modules 61 | coverage 62 | target 63 | npm-debug.log 64 | 65 | /temp -------------------------------------------------------------------------------- /scss-lint-plugin/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Idok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /scss-lint-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | id 'java' 4 | id 'org.jetbrains.intellij' version '0.4.10' 5 | id 'org.jetbrains.kotlin.jvm' version '1.3.41' 6 | } 7 | 8 | group pluginGroup 9 | version pluginVersion 10 | 11 | sourceCompatibility = javaVersion 12 | targetCompatibility = javaTargetVersion 13 | 14 | repositories { 15 | mavenCentral() 16 | // jcenter() 17 | } 18 | 19 | dependencies { 20 | testCompile group: 'junit', name: 'junit', version: '4.12' 21 | // compile (name:'com.wix.intellij-common', ext:'aar') 22 | // compile name: 'intellij-common' 23 | // compile project('intellij-common') 24 | compile files('../intellij-common/build/libs/intellij-common-1.0-SNAPSHOT.jar') 25 | compile 'com.google.code.gson:gson:2.8.5', 'com.esotericsoftware.yamlbeans:yamlbeans:1.08' 26 | //, files('./lib/gson-2.3.1.jar') 27 | } 28 | 29 | // See https://github.com/JetBrains/gradle-intellij-plugin/ 30 | intellij { 31 | pluginName 'scss-lint' 32 | localPath '/Users/idok/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/192.6262.9/IntelliJ IDEA 2019.2 EAP.app' 33 | // alternativeIdePath idePath 34 | // version '192.5728.98' 35 | // type 'IU' 36 | // version 'IC-2018.3' 37 | // plugins = [project(':intellij-common')] 38 | // plugins = ['coverage', 'org.intellij.plugins.markdown:8.5.0.20160208'] 39 | plugins = ['CSS'] 40 | updateSinceUntilBuild false 41 | sameSinceUntilBuild true 42 | } 43 | 44 | publishPlugin { 45 | token intellijPublishToken 46 | channels publishChannel 47 | } 48 | 49 | patchPluginXml { 50 | // changeNotes """ 51 | // Add change notes here.
52 | // most HTML tags may be used""" 53 | } 54 | 55 | -------------------------------------------------------------------------------- /scss-lint-plugin/docs/Inspection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idok/scss-lint-plugin/78aa47c12927d5dbdb99ba48052b242d4ec4fdb5/scss-lint-plugin/docs/Inspection.png -------------------------------------------------------------------------------- /scss-lint-plugin/docs/PropertySortRule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idok/scss-lint-plugin/78aa47c12927d5dbdb99ba48052b242d4ec4fdb5/scss-lint-plugin/docs/PropertySortRule.png -------------------------------------------------------------------------------- /scss-lint-plugin/docs/Rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idok/scss-lint-plugin/78aa47c12927d5dbdb99ba48052b242d4ec4fdb5/scss-lint-plugin/docs/Rule.png -------------------------------------------------------------------------------- /scss-lint-plugin/docs/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idok/scss-lint-plugin/78aa47c12927d5dbdb99ba48052b242d4ec4fdb5/scss-lint-plugin/docs/Settings.png -------------------------------------------------------------------------------- /scss-lint-plugin/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /scss-lint-plugin/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /scss-lint-plugin/lib/xstream-1.4.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idok/scss-lint-plugin/78aa47c12927d5dbdb99ba48052b242d4ec4fdb5/scss-lint-plugin/lib/xstream-1.4.7.jar -------------------------------------------------------------------------------- /scss-lint-plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'scss-lint' 2 | include 'intellij-common' 3 | 4 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/log4j-old.properties: -------------------------------------------------------------------------------- 1 | 2 | # Set root logger level to DEBUG and its only appender to A1. 3 | log4j.rootLogger=DEBUG, A1 4 | 5 | log4j.com.eslint=ALL, A1 6 | 7 | # A1 is set to be a ConsoleAppender. 8 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 9 | 10 | # A1 uses PatternLayout. 11 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 12 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 13 | 14 | #com.intellij.ide.plugins.PluginBean 15 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/log4j0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/EditSettingsAction.java: -------------------------------------------------------------------------------- 1 | package com.scss; 2 | 3 | import com.intellij.codeInsight.intention.HighPriorityAction; 4 | import com.intellij.codeInsight.intention.IntentionAction; 5 | import com.intellij.icons.AllIcons.General; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.application.ModalityState; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.util.Iconable; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.util.IncorrectOperationException; 13 | 14 | import javax.swing.Icon; 15 | 16 | import com.scss.settings.ScssLintSettingsPage; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | class EditSettingsAction implements IntentionAction, Iconable, HighPriorityAction { 20 | private static boolean ourInvoked; 21 | private final boolean fileLevelAnnotation; 22 | private final Icon icon; 23 | private final ScssLintSettingsPage configurable; 24 | 25 | EditSettingsAction(@NotNull ScssLintSettingsPage configurable) { 26 | this(configurable, false, General.Settings); 27 | } 28 | 29 | public EditSettingsAction(@NotNull ScssLintSettingsPage configurable, @NotNull Icon icon) { 30 | this(configurable, false, icon); 31 | } 32 | 33 | public EditSettingsAction(@NotNull ScssLintSettingsPage configurable, boolean fileLevelAnnotation) { 34 | this(configurable, fileLevelAnnotation, General.Settings); 35 | } 36 | 37 | private EditSettingsAction(@NotNull ScssLintSettingsPage configurable, boolean fileLevelAnnotation, @NotNull Icon icon) { 38 | this.configurable = configurable; 39 | this.fileLevelAnnotation = fileLevelAnnotation; 40 | this.icon = icon; 41 | } 42 | 43 | @NotNull 44 | public Priority getPriority() { 45 | return Priority.HIGH; 46 | } 47 | 48 | public Icon getIcon(@IconFlags int flags) { 49 | return this.icon; 50 | } 51 | 52 | @NotNull 53 | public String getText() { 54 | return this.fileLevelAnnotation ? "Settings..." : this.configurable.getDisplayName() + " settings..."; 55 | } 56 | 57 | @NotNull 58 | public String getFamilyName() { 59 | return this.getText(); 60 | } 61 | 62 | public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { 63 | return true; 64 | } 65 | 66 | public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { 67 | if (!ourInvoked) { 68 | ourInvoked = true; 69 | ApplicationManager.getApplication().invokeLater(() -> { 70 | EditSettingsAction.ourInvoked = false; 71 | EditSettingsAction.this.configurable.showSettings(); 72 | }, ModalityState.any()); 73 | } 74 | } 75 | 76 | public boolean startInWriteAction() { 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/ScssLintBundle.java: -------------------------------------------------------------------------------- 1 | package com.scss; 2 | 3 | import com.intellij.CommonBundle; 4 | import org.jetbrains.annotations.NonNls; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.PropertyKey; 7 | 8 | import java.lang.ref.Reference; 9 | import java.lang.ref.SoftReference; 10 | import java.util.ResourceBundle; 11 | 12 | public final class ScssLintBundle { 13 | 14 | public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) { 15 | return CommonBundle.message(getBundle(), key, params); 16 | } 17 | 18 | @NonNls 19 | public static final String BUNDLE = "com.scss.ScssLintBundle"; 20 | private static Reference ourBundle; 21 | 22 | @NonNls 23 | public static final String LOG_ID = "#com.scss"; 24 | 25 | private ScssLintBundle() { 26 | } 27 | 28 | private static ResourceBundle getBundle() { 29 | ResourceBundle bundle = com.intellij.reference.SoftReference.dereference(ourBundle); 30 | if (bundle == null) { 31 | bundle = ResourceBundle.getBundle(BUNDLE); 32 | ourBundle = new SoftReference<>(bundle); 33 | } 34 | return bundle; 35 | } 36 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/ScssLintExternalAnnotator.java: -------------------------------------------------------------------------------- 1 | package com.scss; 2 | 3 | import com.intellij.codeInsight.daemon.HighlightDisplayKey; 4 | import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; 5 | import com.intellij.lang.annotation.Annotation; 6 | import com.intellij.lang.annotation.AnnotationHolder; 7 | import com.intellij.lang.annotation.ExternalAnnotator; 8 | import com.intellij.lang.annotation.HighlightSeverity; 9 | import com.intellij.notification.NotificationType; 10 | import com.intellij.openapi.diagnostic.Logger; 11 | import com.intellij.openapi.editor.Document; 12 | import com.intellij.openapi.editor.Editor; 13 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 14 | import com.intellij.openapi.editor.markup.TextAttributes; 15 | import com.intellij.openapi.project.Project; 16 | import com.intellij.openapi.util.Key; 17 | import com.intellij.openapi.util.TextRange; 18 | import com.intellij.openapi.util.io.FileUtil; 19 | import com.intellij.openapi.util.text.StringUtil; 20 | import com.intellij.openapi.vfs.VirtualFile; 21 | import com.intellij.profile.codeInspection.InspectionProjectProfileManager; 22 | import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider; 23 | import com.intellij.psi.PsiDocumentManager; 24 | import com.intellij.psi.PsiElement; 25 | import com.intellij.psi.PsiFile; 26 | import com.scss.annotator.BaseActionFix; 27 | import com.scss.annotator.Fixes; 28 | import com.scss.config.ScssLintConfigFileChangeTracker; 29 | import com.scss.settings.ScssLintSettingsPage; 30 | import com.scss.utils.ScssLintRunner; 31 | import com.scss.utils.scssLint.Lint; 32 | import com.scss.utils.scssLint.LintResult; 33 | import com.wix.annotator.AnnotatorUtils; 34 | import com.wix.files.ActualFileManager; 35 | import com.wix.files.BaseActualFile; 36 | import com.wix.files.TempFile; 37 | import com.wix.files.ThreadLocalTempActualFile; 38 | import com.wix.utils.PsiUtil; 39 | import org.apache.commons.lang.StringUtils; 40 | import org.jetbrains.annotations.NotNull; 41 | import org.jetbrains.annotations.Nullable; 42 | 43 | import java.io.File; 44 | import java.io.IOException; 45 | import java.util.List; 46 | 47 | /** 48 | * @author idok 49 | */ 50 | public class ScssLintExternalAnnotator extends ExternalAnnotator { 51 | 52 | // public static final ScssLintExternalAnnotator INSTANCE = new ScssLintExternalAnnotator(); 53 | private static final Logger LOG = Logger.getInstance(ScssLintBundle.LOG_ID); 54 | // private static final Key SCSS_TEMP_FILE_KEY = Key.create("SCSS_TEMP_FILE"); 55 | public static final String SCSS = "scss"; 56 | 57 | @Nullable 58 | @Override 59 | public ScssLintAnnotationInput collectInformation(@NotNull PsiFile file) { 60 | return collectInformation(file, null); 61 | } 62 | 63 | @Nullable 64 | @Override 65 | public ScssLintAnnotationInput collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { 66 | return collectInformation(file, editor); 67 | } 68 | 69 | @NotNull 70 | public static HighlightDisplayKey getHighlightDisplayKeyByClass() { 71 | String id = "ScssLint"; 72 | HighlightDisplayKey key = HighlightDisplayKey.find(id); 73 | if (key == null) { 74 | key = new HighlightDisplayKey(id, id); 75 | } 76 | return key; 77 | } 78 | 79 | @Override 80 | public void apply(@NotNull PsiFile file, ScssLintAnnotationResult annotationResult, @NotNull AnnotationHolder holder) { 81 | if (annotationResult == null) { 82 | return; 83 | } 84 | InspectionProjectProfileManager inspectionProjectProfileManager = InspectionProjectProfileManager.getInstance(file.getProject()); 85 | SeverityRegistrar severityRegistrar = inspectionProjectProfileManager.getSeverityRegistrar(); 86 | HighlightDisplayKey inspectionKey = getHighlightDisplayKeyByClass(); 87 | EditorColorsScheme colorsScheme = annotationResult.input.colorsScheme; 88 | 89 | Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); 90 | if (document == null) { 91 | return; 92 | } 93 | 94 | if (annotationResult.fileLevel != null) { 95 | Annotation annotation = holder.createWarningAnnotation(file, annotationResult.fileLevel); 96 | annotation.registerFix(new EditSettingsAction(new ScssLintSettingsPage(file.getProject()))); 97 | annotation.setFileLevelAnnotation(true); 98 | return; 99 | } 100 | 101 | // TODO consider adding a fix to edit configuration file 102 | if (annotationResult.result == null || annotationResult.result.lint == null || annotationResult.result.lint.isEmpty()) { 103 | return; 104 | } 105 | // String relativeFile = FileUtils.makeRelative(file.getProject(), file.getVirtualFile()); 106 | List issues = annotationResult.result.lint.values().iterator().next(); 107 | if (issues == null) { 108 | return; 109 | } 110 | ScssLintProjectComponent component = annotationResult.input.project.getComponent(ScssLintProjectComponent.class); 111 | int tabSize = 4; 112 | for (Lint.Issue issue : issues) { 113 | HighlightSeverity severity = getHighlightSeverity(issue, component.treatAsWarnings); 114 | TextAttributes forcedTextAttributes = AnnotatorUtils.getTextAttributes(colorsScheme, severityRegistrar, severity); 115 | Annotation annotation = createAnnotation(holder, file, document, issue, "SCSS Lint: ", tabSize, severity, forcedTextAttributes, inspectionKey, false); 116 | if (annotation != null) { 117 | int offset = StringUtil.lineColToOffset(document.getText(), issue.line - 1, issue.column); 118 | PsiElement lit = PsiUtil.getElementAtOffset(file, offset); 119 | BaseActionFix actionFix = Fixes.getFixForRule(issue.linter, lit); 120 | if (actionFix != null) { 121 | annotation.registerFix(actionFix, null, inspectionKey); 122 | } 123 | // annotation.registerFix(new SuppressActionFix(issue.rule, lit), null, inspectionKey); 124 | } 125 | } 126 | } 127 | 128 | private static HighlightSeverity getHighlightSeverity(Lint.Issue warn) { 129 | return warn.severity.equals("error") ? HighlightSeverity.ERROR : HighlightSeverity.WARNING; 130 | } 131 | 132 | private static HighlightSeverity getHighlightSeverity(Lint.Issue issue, boolean treatAsWarnings) { 133 | return treatAsWarnings ? HighlightSeverity.WARNING : getHighlightSeverity(issue); 134 | } 135 | 136 | @Nullable 137 | private static Annotation createAnnotation(@NotNull AnnotationHolder holder, @NotNull PsiFile file, @NotNull Document document, @NotNull Lint.Issue issue, 138 | @NotNull String messagePrefix, int tabSize, @NotNull HighlightSeverity severity, @Nullable TextAttributes forcedTextAttributes, 139 | @NotNull HighlightDisplayKey inspectionKey, boolean showErrorOnWholeLine) { 140 | int errorLine = issue.line - 1; 141 | int errorColumn = issue.column - 1; 142 | 143 | if (errorLine < 0 || errorLine >= document.getLineCount()) { 144 | return null; 145 | } 146 | int lineEndOffset = document.getLineEndOffset(errorLine); 147 | int lineStartOffset = document.getLineStartOffset(errorLine); 148 | 149 | int errorLineStartOffset = PsiUtil.calcErrorStartOffsetInDocument(document, lineStartOffset, lineEndOffset, errorColumn, tabSize); 150 | 151 | if (errorLineStartOffset == -1) { 152 | return null; 153 | } 154 | PsiElement element = file.findElementAt(errorLineStartOffset); 155 | // if (element != null /*&& JSInspection.isSuppressedForStatic(element, getInspectionClass(), inspectionKey.getID())*/) 156 | // return null; 157 | TextRange range; 158 | if (showErrorOnWholeLine) { 159 | range = new TextRange(lineStartOffset, lineEndOffset); 160 | } else { 161 | // int offset = StringUtil.lineColToOffset(document.getText(), warn.line - 1, warn.column); 162 | PsiElement lit = PsiUtil.getElementAtOffset(file, errorLineStartOffset); 163 | range = lit.getTextRange(); 164 | // range = new TextRange(errorLineStartOffset, errorLineStartOffset + 1); 165 | } 166 | range = new TextRange(errorLineStartOffset, errorLineStartOffset + issue.length); 167 | 168 | Annotation annotation = createAnnotation(holder, severity, forcedTextAttributes, range, messagePrefix + issue.reason.trim() + " (" + (issue.linter == null ? "none" : issue.linter) + ')'); 169 | if (annotation != null) { 170 | annotation.setAfterEndOfLine(errorLineStartOffset == lineEndOffset); 171 | } 172 | return annotation; 173 | } 174 | 175 | @NotNull 176 | public static Annotation createAnnotation(@NotNull AnnotationHolder holder, @NotNull HighlightSeverity severity, @NotNull TextRange range, @NotNull String message) { 177 | /* 178 | avoid using 179 | holder.createAnnotation(severity, range, message); as it is not supported in PhpStorm: 7.1.3 (PS-133.982) 180 | https://github.com/idok/scss-lint-plugin/issues/5 181 | */ 182 | if (severity.equals(HighlightSeverity.ERROR)) { 183 | return holder.createErrorAnnotation(range, message); 184 | } 185 | return holder.createWarningAnnotation(range, message); 186 | } 187 | 188 | public static Annotation createAnnotation(@NotNull AnnotationHolder holder, @NotNull HighlightSeverity severity, @Nullable TextAttributes forcedTextAttributes, @NotNull TextRange range, @NotNull String message) { 189 | if (forcedTextAttributes != null) { 190 | Annotation annotation = createAnnotation(holder, severity, range, message); 191 | annotation.setEnforcedTextAttributes(forcedTextAttributes); 192 | return annotation; 193 | } 194 | return createAnnotation(holder, severity, range, message); 195 | } 196 | 197 | @Nullable 198 | private static ScssLintAnnotationInput collectInformation(@NotNull PsiFile psiFile, @Nullable Editor editor) { 199 | if (psiFile.getContext() != null || !isScssFile(psiFile)) { 200 | return null; 201 | } 202 | VirtualFile virtualFile = psiFile.getVirtualFile(); 203 | if (virtualFile == null || !virtualFile.isInLocalFileSystem()) { 204 | return null; 205 | } 206 | if (psiFile.getViewProvider() instanceof MultiplePsiFilesPerDocumentFileViewProvider) { 207 | return null; 208 | } 209 | Project project = psiFile.getProject(); 210 | // ScssLintProjectComponent component = project.getComponent(ScssLintProjectComponent.class); 211 | // if (!component.isSettingsValid() || !component.isEnabled()) { 212 | // return new ScssLintAnnotationInput(project, psiFile, null, null, "Invalid settings!"); 213 | // } 214 | Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); 215 | if (document == null) { 216 | return null; 217 | } 218 | String fileContent = document.getText(); 219 | if (StringUtil.isEmptyOrSpaces(fileContent)) { 220 | return null; 221 | } 222 | EditorColorsScheme colorsScheme = editor == null ? null : editor.getColorsScheme(); 223 | return new ScssLintAnnotationInput(project, psiFile, fileContent, colorsScheme); 224 | } 225 | 226 | private static boolean isScssFile(PsiFile file) { 227 | return file.getVirtualFile().getExtension().equals(SCSS); 228 | // return file instanceof SCSSFile && file.getFileType().equals(SCSSFileType.SCSS); 229 | } 230 | 231 | private static final Key TEMP_FILE = Key.create("SCSS_LINT_TEMP_FILE"); 232 | 233 | private static void copyConfig(Project project, File temp) throws IOException { 234 | copyConfigFile(project, temp, ScssLintConfigFileChangeTracker.SCSS_LINT_YML); 235 | } 236 | 237 | private static void copyConfigFile(Project project, File temp, String fileName) throws IOException { 238 | VirtualFile jscs = project.getBaseDir().findChild(fileName); 239 | File tempJscs = new File(temp, fileName); 240 | if (jscs != null) { 241 | //check if stale? 242 | FileUtil.copy(new File(jscs.getPath()), tempJscs); 243 | tempJscs.deleteOnExit(); 244 | } 245 | } 246 | 247 | @Nullable 248 | @Override 249 | public ScssLintAnnotationResult doAnnotate(ScssLintAnnotationInput collectedInfo) { 250 | BaseActualFile actualFile = null; 251 | try { 252 | PsiFile file = collectedInfo.psiFile; 253 | if (!isScssFile(file)) { 254 | return null; 255 | } 256 | ScssLintProjectComponent component = file.getProject().getComponent(ScssLintProjectComponent.class); 257 | if (!component.isEnabled()) { 258 | if (component.isDismissConfigurationHints()) { 259 | return null; 260 | } 261 | return new ScssLintAnnotationResult(collectedInfo, null, "SCSS Lint is available for this file but is not configured"); 262 | } 263 | if (!component.isSettingsValid()) { 264 | return new ScssLintAnnotationResult(collectedInfo, null, "SCSS Lint is not configured correctly"); 265 | } 266 | 267 | ScssLintConfigFileChangeTracker.getInstance(collectedInfo.project).startIfNeeded(); 268 | actualFile = ActualFileManager.getOrCreateActualFile(TEMP_FILE, file, collectedInfo.fileContent); 269 | if (actualFile instanceof TempFile) { 270 | copyConfig(file.getProject(), new File(actualFile.getCwd())); 271 | } 272 | if (actualFile == null) { 273 | LOG.warn("Failed to create file for lint"); 274 | return null; 275 | } 276 | LintResult result = ScssLintRunner.runLint(actualFile.getCwd(), actualFile.getPath(), component.scssLintExecutable, component.scssLintConfigFile); 277 | 278 | if (StringUtils.isNotEmpty(result.errorOutput)) { 279 | component.showInfoNotification(result.errorOutput, NotificationType.WARNING); 280 | return null; 281 | } 282 | Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); 283 | if (document == null) { 284 | component.showInfoNotification("Error running SCSS Lint inspection: Could not get document for file " + file.getName(), NotificationType.WARNING); 285 | LOG.warn("Could not get document for file " + file.getName()); 286 | return null; 287 | } 288 | return new ScssLintAnnotationResult(collectedInfo, result); 289 | } catch (Exception e) { 290 | LOG.error("Error running ScssLint inspection: ", e); 291 | ScssLintProjectComponent.showNotification("Error running SCSS Lint inspection: " + e.getMessage(), NotificationType.ERROR); 292 | } finally { 293 | ActualFileManager.dispose(actualFile); 294 | } 295 | return null; 296 | } 297 | } 298 | 299 | class ScssLintAnnotationInput { 300 | public final String fileContent; 301 | public final EditorColorsScheme colorsScheme; 302 | public final Project project; 303 | public final PsiFile psiFile; 304 | 305 | public ScssLintAnnotationInput(Project project, PsiFile psiFile, String fileContent, EditorColorsScheme colorsScheme) { 306 | this.project = project; 307 | this.psiFile = psiFile; 308 | this.fileContent = fileContent; 309 | this.colorsScheme = colorsScheme; 310 | } 311 | } 312 | 313 | class ScssLintAnnotationResult { 314 | public ScssLintAnnotationResult(ScssLintAnnotationInput input, LintResult result) { 315 | this.input = input; 316 | this.result = result; 317 | } 318 | 319 | public ScssLintAnnotationResult(ScssLintAnnotationInput input, LintResult result, String fileLevel) { 320 | this.input = input; 321 | this.result = result; 322 | this.fileLevel = fileLevel; 323 | } 324 | 325 | public final ScssLintAnnotationInput input; 326 | public final LintResult result; 327 | public String fileLevel; 328 | } 329 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/ScssLintInspection.java: -------------------------------------------------------------------------------- 1 | package com.scss; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.intellij.codeInspection.*; 5 | import com.intellij.codeInspection.ex.UnfairLocalInspectionTool; 6 | import com.intellij.ide.DataManager; 7 | import com.intellij.openapi.actionSystem.CommonDataKeys; 8 | import com.intellij.openapi.actionSystem.DataContext; 9 | import com.intellij.openapi.diagnostic.Logger; 10 | //import com.intellij.openapi.options.Configurable; 11 | //import com.intellij.openapi.options.ex.Settings; 12 | //import com.intellij.openapi.options.newEditor.OptionsEditor; 13 | //import com.intellij.openapi.options.newEditor.OptionsEditorContext; 14 | import com.intellij.openapi.project.Project; 15 | import com.intellij.openapi.util.Key; 16 | import com.intellij.psi.PsiElement; 17 | import com.intellij.psi.PsiElementVisitor; 18 | import com.intellij.psi.PsiFile; 19 | import com.intellij.ui.HyperlinkAdapter; 20 | import com.intellij.ui.HyperlinkLabel; 21 | import com.intellij.ui.IdeBorderFactory; 22 | import com.intellij.util.containers.ContainerUtil; 23 | import com.scss.settings.ScssLintSettingsPage; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | 27 | import javax.swing.*; 28 | import javax.swing.event.HyperlinkEvent; 29 | import java.awt.*; 30 | import java.util.List; 31 | 32 | //import org.jetbrains.plugins.sass.SASSBundle; 33 | 34 | public class ScssLintInspection extends LocalInspectionTool implements BatchSuppressableTool, UnfairLocalInspectionTool { //extends PropertySuppressableInspectionBase { 35 | 36 | private static final String INSPECTION_SHORT_NAME = "ScssLintInspection"; 37 | public static final Key KEY = Key.create(INSPECTION_SHORT_NAME); 38 | 39 | private static final Logger LOG = Logger.getInstance(ScssLintBundle.LOG_ID); 40 | 41 | @NotNull 42 | public String getDisplayName() { 43 | return ScssLintBundle.message("scss.property.inspection.display.name"); 44 | } 45 | 46 | @NotNull 47 | public String getShortName() { 48 | return INSPECTION_SHORT_NAME; 49 | } 50 | 51 | 52 | public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) { 53 | return ExternalAnnotatorInspectionVisitor.checkFileWithExternalAnnotator(file, manager, isOnTheFly, new ScssLintExternalAnnotator()); 54 | } 55 | 56 | @NotNull 57 | @Override 58 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { 59 | return new ExternalAnnotatorInspectionVisitor(holder, new ScssLintExternalAnnotator(), isOnTheFly); 60 | } 61 | 62 | public JComponent createOptionsPanel() { 63 | JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 64 | HyperlinkLabel settingsLink = createHyperLink(); 65 | panel.setBorder(IdeBorderFactory.createTitledBorder(getDisplayName() + " options")); 66 | panel.add(settingsLink); 67 | return panel; 68 | } 69 | 70 | @NotNull 71 | public String getId() { 72 | return "Settings.JavaScript.Linters.ScssLint"; 73 | } 74 | 75 | @NotNull 76 | private HyperlinkLabel createHyperLink() { 77 | // List path = ContainerUtil.newArrayList(JSBundle.message("settings.javascript.root.configurable.name"), JSBundle.message("settings.javascript.linters.configurable.name"), getDisplayName()); 78 | List path = ContainerUtil.newArrayList(ScssLintBundle.message("scss.inspections.group.name"), ScssLintBundle.message("scss.inspection.group.name"), getDisplayName()); 79 | String title = Joiner.on(" / ").join(path); 80 | final HyperlinkLabel settingsLink = new HyperlinkLabel(title); 81 | settingsLink.addHyperlinkListener(new HyperlinkAdapter() { 82 | public void hyperlinkActivated(HyperlinkEvent e) { 83 | DataContext dataContext = DataManager.getInstance().getDataContext(settingsLink); 84 | Project project = CommonDataKeys.PROJECT.getData(dataContext); 85 | if (project != null) { 86 | showSettings(project); 87 | // } else { 88 | // new ScssLintSettingsPage(null).showSettings(); 89 | } 90 | 91 | // Settings settings = (Settings) Settings.KEY.getData(dataContext); 92 | // if (settings == null) { 93 | // configurable.showEditDialog(); 94 | // } else { 95 | // settings.select(settings.find(getId())); 96 | // } 97 | // 98 | // OptionsEditor optionsEditor = OptionsEditor.KEY.getData(dataContext); 99 | // if (optionsEditor == null) { 100 | // Project project = CommonDataKeys.PROJECT.getData(dataContext); 101 | // if (project != null) { 102 | // showSettings(project); 103 | // } 104 | // return; 105 | // } 106 | // Configurable configurable = optionsEditor.findConfigurableById(ScssLintInspection.this.getId()); 107 | // if (configurable != null) { 108 | // optionsEditor.clearSearchAndSelect(configurable); 109 | // } 110 | // 111 | // 112 | // 113 | // Project project = CommonDataKeys.PROJECT.getData(dataContext); 114 | // if(project == null) { 115 | // LOG.warn("No project found in data context"); 116 | // } else { 117 | // JSLinterConfigurable configurable = JSLinterInspection.this.getExternalAnnotatorForBatchInspection().createSettingsConfigurable(project); 118 | // Settings settings = (Settings)Settings.KEY.getData(dataContext); 119 | // if(settings == null) { 120 | // configurable.showEditDialog(); 121 | // } else { 122 | // settings.select(settings.find(configurable.getId())); 123 | // } 124 | // } 125 | } 126 | }); 127 | return settingsLink; 128 | } 129 | 130 | static void showSettings(Project project) { 131 | ScssLintSettingsPage configurable = new ScssLintSettingsPage(project); 132 | configurable.showSettings(); 133 | // String dimensionKey = ShowSettingsUtilImpl.createDimensionKey(configurable); 134 | // SingleConfigurableEditor singleConfigurableEditor = new SingleConfigurableEditor(project, configurable, dimensionKey, false); 135 | // singleConfigurableEditor.show(); 136 | } 137 | 138 | @Override 139 | public boolean isSuppressedFor(@NotNull PsiElement element) { 140 | return false; 141 | } 142 | 143 | @NotNull 144 | @Override 145 | public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) { 146 | return new SuppressQuickFix[0]; 147 | } 148 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/ScssLintProjectComponent.java: -------------------------------------------------------------------------------- 1 | package com.scss; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationListener; 5 | import com.intellij.notification.NotificationType; 6 | import com.intellij.notification.Notifications; 7 | import com.intellij.openapi.components.ProjectComponent; 8 | import com.intellij.openapi.diagnostic.Logger; 9 | import com.intellij.openapi.project.Project; 10 | import com.scss.settings.Settings; 11 | import com.wix.utils.FileUtils; 12 | import com.wix.utils.FileUtils.ValidationStatus; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | public class ScssLintProjectComponent implements ProjectComponent { 16 | public static final String FIX_CONFIG_HREF = "\nFix Configuration"; 17 | protected final Project project; 18 | protected Settings settings; 19 | protected boolean settingValidStatus; 20 | protected String settingValidVersion; 21 | protected String settingVersionLastShowNotification; 22 | 23 | private static final Logger LOG = Logger.getInstance(ScssLintBundle.LOG_ID); 24 | 25 | public String scssLintConfigFile; 26 | public String scssLintExecutable; 27 | public boolean treatAsWarnings; 28 | public boolean pluginEnabled; 29 | public boolean dismissConfigurationHints; 30 | 31 | public static final String PLUGIN_NAME = "SCSS Lint"; 32 | 33 | public ScssLintProjectComponent(Project project) { 34 | this.project = project; 35 | settings = Settings.getInstance(project); 36 | } 37 | 38 | @Override 39 | public void projectOpened() { 40 | if (isEnabled()) { 41 | isSettingsValid(); 42 | } 43 | } 44 | 45 | @Override 46 | public void projectClosed() { 47 | } 48 | 49 | @Override 50 | public void initComponent() { 51 | if (isEnabled()) { 52 | isSettingsValid(); 53 | } 54 | } 55 | 56 | @Override 57 | public void disposeComponent() { 58 | } 59 | 60 | @NotNull 61 | @Override 62 | public String getComponentName() { 63 | return ScssLintProjectComponent.class.getName(); 64 | } 65 | 66 | public boolean isEnabled() { 67 | return Settings.getInstance(project).pluginEnabled; 68 | } 69 | 70 | public boolean isDismissConfigurationHints() { 71 | return Settings.getInstance(project).dismissConfigurationHints; 72 | } 73 | 74 | public boolean isSettingsValid() { 75 | if (!settings.getVersion().equals(settingValidVersion)) { 76 | validateSettings(); 77 | settingValidVersion = settings.getVersion(); 78 | } 79 | return settingValidStatus; 80 | } 81 | 82 | public void validateSettings() { 83 | settingValidStatus = isValid(); 84 | if (!settingValidStatus) { 85 | return; 86 | } 87 | 88 | // if (StringUtil.isNotEmpty(settings.scssLintExecutable)) { 89 | // File file = new File(project.getBasePath(), settings.scssLintExecutable); 90 | // if (!file.exists()) { 91 | // showErrorConfigNotification(ESLintBundle.message("eslint.rules.dir.does.not.exist", file.toString())); 92 | // LOG.debug("Rules directory not found"); 93 | // settingValidStatus = false; 94 | // return false; 95 | // } 96 | // } 97 | scssLintExecutable = settings.scssLintExecutable; 98 | scssLintConfigFile = settings.scssLintConfigFile; 99 | treatAsWarnings = settings.treatAllIssuesAsWarnings; 100 | pluginEnabled = settings.pluginEnabled; 101 | dismissConfigurationHints = settings.dismissConfigurationHints; 102 | } 103 | 104 | public boolean isValid() { 105 | // do not validate if disabled 106 | if (!settings.pluginEnabled) { 107 | return true; 108 | } 109 | // boolean status = validateField("Node Interpreter", settings.nodeInterpreter, true, false, true); 110 | // if (!status) { 111 | // return false; 112 | // } 113 | // status = validateField("Rules", settings.rulesPath, false, true, false); 114 | // if (!status) { 115 | // return false; 116 | // } 117 | boolean status = validateField("SCSS Lint bin", settings.scssLintExecutable, false, false, true); 118 | if (!status) { 119 | return false; 120 | } 121 | return true; 122 | } 123 | 124 | private boolean validateField(String fieldName, String value, boolean shouldBeAbsolute, boolean allowEmpty, boolean isFile) { 125 | ValidationStatus r = FileUtils.validateProjectPath(shouldBeAbsolute ? null : project, value, allowEmpty, isFile); 126 | if (r == ValidationStatus.IS_EMPTY && !allowEmpty) { 127 | String msg = ScssLintBundle.message("scss.path.is.empty", fieldName); 128 | validationFailed(msg); 129 | return false; 130 | } 131 | if (isFile) { 132 | if (r == ValidationStatus.NOT_A_FILE) { 133 | String msg = ScssLintBundle.message("scss.file.is.not.a.file", fieldName, value); 134 | validationFailed(msg); 135 | return false; 136 | } 137 | } else { 138 | if (r == ValidationStatus.NOT_A_DIRECTORY) { 139 | String msg = ScssLintBundle.message("scss.directory.is.not.a.dir", fieldName, value); 140 | validationFailed(msg); 141 | return false; 142 | } 143 | } 144 | if (r == ValidationStatus.DOES_NOT_EXIST) { 145 | String msg = ScssLintBundle.message("scss.file.does.not.exist", fieldName, value); 146 | validationFailed(msg); 147 | return false; 148 | } 149 | return true; 150 | } 151 | 152 | private void validationFailed(String msg) { 153 | NotificationListener notificationListener = (notification, event) -> ScssLintInspection.showSettings(project); 154 | String errorMessage = msg + FIX_CONFIG_HREF; 155 | showInfoNotification(errorMessage, NotificationType.WARNING, notificationListener); 156 | LOG.debug(msg); 157 | settingValidStatus = false; 158 | } 159 | 160 | protected void showErrorConfigNotification(String content) { 161 | if (!settings.getVersion().equals(settingVersionLastShowNotification)) { 162 | settingVersionLastShowNotification = settings.getVersion(); 163 | showInfoNotification(content, NotificationType.WARNING); 164 | } 165 | } 166 | 167 | public void showInfoNotification(String content, NotificationType type) { 168 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type); 169 | Notifications.Bus.notify(errorNotification, this.project); 170 | } 171 | 172 | public void showInfoNotification(String content, NotificationType type, NotificationListener notificationListener) { 173 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type, notificationListener); 174 | Notifications.Bus.notify(errorNotification, this.project); 175 | } 176 | 177 | public static void showNotification(String content, NotificationType type) { 178 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type); 179 | Notifications.Bus.notify(errorNotification); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/ThreadLocalActualFile.java: -------------------------------------------------------------------------------- 1 | //package com.scss; 2 | // 3 | //import com.intellij.openapi.diagnostic.Logger; 4 | //import com.intellij.openapi.util.io.FileUtil; 5 | //import com.intellij.openapi.vfs.VirtualFile; 6 | //import com.wix.utils.FileUtils; 7 | //import org.jetbrains.annotations.NotNull; 8 | //import org.jetbrains.annotations.Nullable; 9 | // 10 | //import java.io.File; 11 | //import java.io.IOException; 12 | // 13 | ///** 14 | // * Lint target file thread local storage 15 | // */ 16 | //class ThreadLocalActualFile extends ThreadLocal { 17 | // private final String baseName; 18 | // private final String extension; 19 | // private final VirtualFile file; 20 | // private static final Logger LOG = Logger.getInstance(ScssLintBundle.LOG_ID); 21 | // 22 | // public boolean isTemp; 23 | // 24 | // private static final String SCSS_LINT_TMP = "_scsslint_tmp"; 25 | // private static final String TEMP_DIR_NAME = "intellij-scsslint-temp"; 26 | // 27 | // ThreadLocalActualFile(@NotNull VirtualFile file) { 28 | // this.baseName = file.getNameWithoutExtension(); 29 | // this.extension = FileUtils.getExtensionWithDot(file); 30 | // this.file = file; 31 | // } 32 | // 33 | // @Nullable 34 | // public File getOrCreateFile() { 35 | // String path = super.get(); 36 | // if (path != null) { 37 | // File file = new File(path); 38 | // if (file.isFile()) { 39 | // return file; 40 | // } 41 | // } 42 | // File file = createFile(); 43 | // if (file != null) { 44 | // set(file.getAbsolutePath()); 45 | // return file; 46 | // } 47 | // return null; 48 | // } 49 | // 50 | // @Nullable 51 | // public static File getOrCreateTempDir() { 52 | // File tmpDir = new File(FileUtil.getTempDirectory()); 53 | // File dir = new File(tmpDir, TEMP_DIR_NAME); 54 | // if (dir.isDirectory() || dir.mkdirs()) { 55 | // return dir; 56 | // } 57 | // try { 58 | // return FileUtil.createTempDirectory(tmpDir, TEMP_DIR_NAME, null); 59 | // } catch (IOException ignored) { 60 | // LOG.warn("Can't create '" + TEMP_DIR_NAME + "' temporary directory."); 61 | // } 62 | // return null; 63 | // } 64 | // 65 | // @Nullable 66 | // private File createFile() { 67 | //// File retFile = new File(file.getParent().getPath(), file.getNameWithoutExtension() + "_eslint_tmp." + file.getExtension()); 68 | // File retFile; 69 | // try { 70 | // // try to create a temp file next to original file 71 | // retFile = File.createTempFile(this.baseName + SCSS_LINT_TMP, this.extension, new File(file.getParent().getPath())); 72 | // isTemp = true; 73 | // return retFile; 74 | // } catch (IOException e) { 75 | // LOG.warn("Can not create temp file", e); 76 | // } 77 | // 78 | // // try to create a temp file in temp folder 79 | // File dir = getOrCreateTempDir(); 80 | // if (dir == null) { 81 | // return null; 82 | // } 83 | // File file = new File(dir, this.baseName + this.extension); 84 | // boolean created = false; 85 | // if (!file.exists()) { 86 | // try { 87 | // created = file.createNewFile(); 88 | // } catch (IOException ignored) { 89 | // LOG.warn("Can not create " + file.getAbsolutePath()); 90 | // } 91 | // } 92 | // if (!created) { 93 | // try { 94 | // file = FileUtil.createTempFile(dir, this.baseName, this.extension); 95 | // } catch (IOException e) { 96 | // LOG.warn("Can not create temp file", e); 97 | // return null; 98 | // } 99 | // } 100 | // file.deleteOnExit(); 101 | // return file; 102 | // } 103 | //} 104 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/annotator/BaseActionFix.java: -------------------------------------------------------------------------------- 1 | package com.scss.annotator; 2 | 3 | import com.intellij.codeInsight.intention.HighPriorityAction; 4 | import com.intellij.codeInsight.intention.IntentionAction; 5 | import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @author idok 15 | */ 16 | public abstract class BaseActionFix extends LocalQuickFixAndIntentionActionOnPsiElement implements IntentionAction, HighPriorityAction { 17 | public BaseActionFix(PsiElement element) { 18 | super(element); 19 | } 20 | 21 | @NotNull 22 | @Override 23 | public String getFamilyName() { 24 | return getText(); 25 | } 26 | 27 | protected abstract void fix(@NotNull Project project, Editor editor, PsiFile file, PsiElement start); 28 | 29 | public void invoke(@NotNull Project project, @NotNull PsiFile file, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement start, @NotNull PsiElement end) { 30 | fix(project, editor, file, start); 31 | } 32 | 33 | @Override 34 | public boolean startInWriteAction() { 35 | return true; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/annotator/Fixes.java: -------------------------------------------------------------------------------- 1 | package com.scss.annotator; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public final class Fixes { 7 | private Fixes() { 8 | } 9 | 10 | @Nullable 11 | public static BaseActionFix getFixForRule(String rule, PsiElement element) { 12 | if ("PropertySortOrder".equals(rule)) { 13 | return new PropertySortOrderFix(element); 14 | } 15 | return null; 16 | } 17 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/annotator/PropertySortOrderFix.java: -------------------------------------------------------------------------------- 1 | //package com.scss.annotator; 2 | // 3 | //import com.intellij.openapi.editor.Editor; 4 | //import com.intellij.openapi.project.Project; 5 | //import com.intellij.psi.PsiElement; 6 | //import com.intellij.psi.PsiFile; 7 | //import com.intellij.psi.css.CssBlock; 8 | //import com.intellij.psi.css.CssDeclaration; 9 | //import com.intellij.psi.css.impl.parsing.CssParser; 10 | //import com.intellij.psi.css.impl.util.table.CssPropertyUtil; 11 | //import com.intellij.psi.util.PsiTreeUtil; 12 | //import com.intellij.util.IncorrectOperationException; 13 | //import com.intellij.util.containers.ContainerUtil; 14 | //import com.scss.ScssLintBundle; 15 | //import org.jetbrains.annotations.NotNull; 16 | // 17 | //import java.util.Comparator; 18 | //import java.util.regex.Matcher; 19 | //import java.util.regex.Pattern; 20 | // 21 | ///** 22 | // * @author idok 23 | // */ 24 | //public class PropertySortOrderFix extends BaseActionFix { 25 | // 26 | // private static final String[] VENDOR_PREFIXS = { 27 | // "-moz-" /* Firefox and other browsers using Mozilla's browser engine */, 28 | // "-webkit-" /* Safari, Chrome and browsers using the Webkit engine */, 29 | // "-o-" /* Opera */, 30 | // "-ms-" /* Internet Explorer (but not always) */}; 31 | // 32 | // private static final Pattern COMPILE = Pattern.compile("-(?:moz|o|webkit|ms)-(font)"); 33 | // 34 | // public PropertySortOrderFix(PsiElement element) { 35 | // super(element); 36 | // } 37 | // 38 | // @NotNull 39 | // @Override 40 | // public String getText() { 41 | // return ScssLintBundle.message("inspection.fix.sort"); 42 | // } 43 | // 44 | // public static String strip(String p) { 45 | // Matcher m = COMPILE.matcher(p); 46 | // return m.matches() ? m.group(1) : p; 47 | // } 48 | // 49 | // @Override 50 | // public void fix(@NotNull Project project, Editor editor, PsiFile file, PsiElement start) throws IncorrectOperationException { 51 | // CssBlock block = PsiTreeUtil.getParentOfType(start, CssBlock.class); 52 | // CssDeclaration[] declarations = block.getDeclarations(); 53 | // CssDeclaration[] sorted = new CssDeclaration[declarations.length]; 54 | // for (int i = 0; i < sorted.length; i++) { 55 | // sorted[i] = (CssDeclaration) declarations[i].copy(); 56 | // } 57 | // 58 | // ContainerUtil.sort(sorted, (cssDeclaration, cssDeclaration2) -> { 59 | // String p1 = CssPropertyUtil.getElementNameWithoutVendorPrefix(cssDeclaration.getPropertyName()); 60 | // String v1 = CssPropertyUtil.getVendorPrefix(cssDeclaration.getPropertyName()); 61 | // String p2 = CssPropertyUtil.getElementNameWithoutVendorPrefix(cssDeclaration2.getPropertyName()); 62 | // String v2 = CssPropertyUtil.getVendorPrefix(cssDeclaration2.getPropertyName()); 63 | // return p1.compareTo(p2); 64 | // }); 65 | // 66 | // for (int i = 0; i < declarations.length; i++) { 67 | // declarations[i].replace(sorted[i]); 68 | // } 69 | // } 70 | //} 71 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/annotator/SortProperty.kt: -------------------------------------------------------------------------------- 1 | //package com.scss.annotator 2 | // 3 | //import com.intellij.openapi.editor.Editor 4 | //import com.intellij.openapi.project.Project 5 | //import com.intellij.psi.PsiElement 6 | //import com.intellij.psi.PsiFile 7 | //import com.intellij.psi.css.CssBlock 8 | //import com.intellij.psi.css.CssDeclaration 9 | ////import com.intellij.psi.css.impl.util.table.CssDescriptorsUtil //CssPropertyUtil 10 | //import com.intellij.psi.util.PsiTreeUtil 11 | //import com.intellij.util.IncorrectOperationException 12 | //import com.scss.ScssLintBundle 13 | //import java.util.* 14 | //import java.util.regex.Pattern 15 | // 16 | ///** 17 | // * @author idok 18 | // */ 19 | //class PropertySortOrderFix(element: PsiElement) : BaseActionFix(element) { 20 | // 21 | // override fun getText(): String { 22 | // return ScssLintBundle.message("inspection.fix.sort") 23 | // } 24 | // 25 | // @Throws(IncorrectOperationException::class) 26 | // public override fun fix(project: Project, editor: Editor, file: PsiFile, start: PsiElement) { 27 | // val block = PsiTreeUtil.getParentOfType(start, CssBlock::class.java) ?: return 28 | // val declarations = block.declarations 29 | // val cloned:List = declarations.map { it.copy() as CssDeclaration } 30 | // val sorted = cloned.sortedWith(CssDeclarationComparator()) 31 | // for (i in declarations.indices) { 32 | // declarations[i].replace(sorted[i]) 33 | // } 34 | // } 35 | // 36 | // companion object { 37 | // private val COMPILE = Pattern.compile("-(?:moz|o|webkit|ms)-(font)") 38 | // 39 | // fun strip(p: String): String { 40 | // val m = COMPILE.matcher(p) 41 | // return if (m.matches()) m.group(1) else p 42 | // } 43 | // 44 | // fun parse(it: CssDeclaration?) = parse(it?.propertyName ?: "") 45 | //// fun parse(it: String) = CP2(CssPropertyUtil.getVendorPrefix(it), CssPropertyUtil.getElementNameWithoutVendorPrefix(it)) 46 | // fun parse(it: String) = CP2("", "") 47 | // } 48 | // 49 | // class CssDeclarationComparator : Comparator { 50 | // override fun compare(o1: CssDeclaration?, o2: CssDeclaration?): Int { 51 | // val p1 = parse(o1) 52 | // val p2 = parse(o2) 53 | // return p1.compareTo(p2) 54 | // } 55 | // } 56 | // 57 | // data class CP2(val prefix: String, val name: String) { 58 | // override fun toString() = "$prefix$name" 59 | // 60 | // fun compareTo(o1: CP2): Int { 61 | // return name.compareTo(o1.name) 62 | // } 63 | // } 64 | //} 65 | // 66 | // 67 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/config/ScssLintConfigFileChangeTracker.java: -------------------------------------------------------------------------------- 1 | package com.scss.config; 2 | 3 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.components.ServiceManager; 6 | import com.intellij.openapi.editor.EditorFactory; 7 | import com.intellij.openapi.editor.event.DocumentEvent; 8 | import com.intellij.openapi.editor.event.DocumentListener; 9 | import com.intellij.openapi.editor.event.EditorEventMulticaster; 10 | import com.intellij.openapi.fileEditor.FileDocumentManager; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.vfs.*; 13 | import com.scss.ScssLintProjectComponent; 14 | import com.scss.utils.ScssLintFinder; 15 | import org.jetbrains.annotations.Contract; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import java.util.concurrent.atomic.AtomicBoolean; 19 | 20 | public class ScssLintConfigFileChangeTracker { 21 | private final AtomicBoolean TRACKING = new AtomicBoolean(false); 22 | private final Project project; 23 | 24 | public static final String SCSS_LINT_YML = "." + ScssLintFinder.SCSS_LINT_YML; 25 | 26 | public ScssLintConfigFileChangeTracker(@NotNull Project project) { 27 | this.project = project; 28 | } 29 | 30 | @NotNull 31 | public static ScssLintConfigFileChangeTracker getInstance(@NotNull Project project) { 32 | return ServiceManager.getService(project, ScssLintConfigFileChangeTracker.class); 33 | } 34 | 35 | public void startIfNeeded() { 36 | if (TRACKING.compareAndSet(false, true)) 37 | ApplicationManager.getApplication().invokeLater(new Runnable() { 38 | public void run() { 39 | ApplicationManager.getApplication().runWriteAction(new Runnable() { 40 | public void run() { 41 | VirtualFileManager.getInstance().addVirtualFileListener(new ScssLintConfigFileVfsListener(), ScssLintConfigFileChangeTracker.this.project); 42 | EditorEventMulticaster multicaster = EditorFactory.getInstance().getEventMulticaster(); 43 | multicaster.addDocumentListener(new ScssLintConfigFileDocumentListener(), ScssLintConfigFileChangeTracker.this.project); 44 | } 45 | }); 46 | } 47 | }); 48 | } 49 | 50 | private void onChange(@NotNull VirtualFile file) { 51 | // if (file.getFileType().equals(ScssLintConfigFileType.INSTANCE) && !project.isDisposed()) { 52 | if (SCSS_LINT_YML.equals(file.getName()) && !project.isDisposed()) { 53 | restartCodeAnalyzerIfNeeded(); 54 | } 55 | } 56 | 57 | private void restartCodeAnalyzerIfNeeded() { 58 | ScssLintProjectComponent component = project.getComponent(ScssLintProjectComponent.class); 59 | if (component.isEnabled()) { 60 | DaemonCodeAnalyzer.getInstance(project).restart(); 61 | } 62 | } 63 | 64 | private final class ScssLintConfigFileDocumentListener implements DocumentListener { 65 | private ScssLintConfigFileDocumentListener() { 66 | } 67 | 68 | public void beforeDocumentChange(@NotNull DocumentEvent event) { 69 | } 70 | 71 | public void documentChanged(@NotNull DocumentEvent event) { 72 | VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument()); 73 | if (file != null) { 74 | ScssLintConfigFileChangeTracker.this.onChange(file); 75 | } 76 | } 77 | } 78 | 79 | private final class ScssLintConfigFileVfsListener implements VirtualFileListener { 80 | @Contract(pure = true) 81 | private ScssLintConfigFileVfsListener() { 82 | } 83 | 84 | public void fileCreated(@NotNull VirtualFileEvent event) { 85 | ScssLintConfigFileChangeTracker.this.onChange(event.getFile()); 86 | } 87 | 88 | public void fileDeleted(@NotNull VirtualFileEvent event) { 89 | ScssLintConfigFileChangeTracker.this.onChange(event.getFile()); 90 | } 91 | 92 | public void fileMoved(@NotNull VirtualFileMoveEvent event) { 93 | ScssLintConfigFileChangeTracker.this.onChange(event.getFile()); 94 | } 95 | 96 | public void fileCopied(@NotNull VirtualFileCopyEvent event) { 97 | ScssLintConfigFileChangeTracker.this.onChange(event.getFile()); 98 | ScssLintConfigFileChangeTracker.this.onChange(event.getOriginalFile()); 99 | } 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/config/ScssLintConfigFileType.java: -------------------------------------------------------------------------------- 1 | //package com.scss.config; 2 | // 3 | //import com.intellij.openapi.fileTypes.LanguageFileType; 4 | //import icons.ScssLintIcons; 5 | //import org.jetbrains.annotations.NotNull; 6 | //import org.jetbrains.yaml.YAMLLanguage; 7 | // 8 | //import javax.swing.*; 9 | // 10 | //public class ScssLintConfigFileType extends LanguageFileType { 11 | // public static final ScssLintConfigFileType INSTANCE = new ScssLintConfigFileType(); 12 | // public static final String SCSS_LINT_YML = "scss-lint.yml"; 13 | // public static final String SCSS_LINT_YML_NAME = "." + SCSS_LINT_YML; 14 | // public static final String SCSS_LINT_YAML_NAME = ".scss-lint.yaml"; 15 | // 16 | // public static boolean isScssConfigFile(String name) { 17 | // return name.equals(SCSS_LINT_YML_NAME) || name.equals(SCSS_LINT_YAML_NAME); 18 | // } 19 | // 20 | // private ScssLintConfigFileType() { 21 | // super(YAMLLanguage.INSTANCE); 22 | // } 23 | // 24 | // @NotNull 25 | // public String getName() { 26 | // return "SCSS Lint"; 27 | // } 28 | // 29 | // @NotNull 30 | // public String getDescription() { 31 | // return "SCSS Lint configuration file"; 32 | // } 33 | // 34 | // @NotNull 35 | // public String getDefaultExtension() { 36 | // return SCSS_LINT_YML; 37 | // } 38 | // 39 | // @NotNull 40 | // public Icon getIcon() { 41 | // return ScssLintIcons.ESLint; 42 | // } 43 | //} -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/config/ScssLintConfigFileTypeFactory.java: -------------------------------------------------------------------------------- 1 | //package com.scss.config; 2 | // 3 | //import com.intellij.openapi.fileTypes.ExactFileNameMatcher; 4 | //import com.intellij.openapi.fileTypes.ExtensionFileNameMatcher; 5 | //import com.intellij.openapi.fileTypes.FileTypeConsumer; 6 | //import com.intellij.openapi.fileTypes.FileTypeFactory; 7 | //import org.jetbrains.annotations.NotNull; 8 | // 9 | //public class ScssLintConfigFileTypeFactory extends FileTypeFactory { 10 | // public void createFileTypes(@NotNull FileTypeConsumer consumer) { 11 | // consumer.consume(ScssLintConfigFileType.INSTANCE, 12 | // new ExactFileNameMatcher(ScssLintConfigFileType.SCSS_LINT_YML_NAME), 13 | // new ExactFileNameMatcher(ScssLintConfigFileType.SCSS_LINT_YAML_NAME)); 14 | //// consumer.consume(ScssLintConfigFileType.INSTANCE, new ExtensionFileNameMatcher(ScssLintConfigFileType.SCSS_LINT_YML)); 15 | // //, new ExactFileNameMatcher("eslint.json") 16 | // } 17 | //} -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/config/ScssLintConfigFileUtil.java: -------------------------------------------------------------------------------- 1 | //package com.scss.config; 2 | // 3 | //import com.intellij.openapi.vfs.VirtualFile; 4 | //import com.intellij.psi.PsiElement; 5 | //import com.intellij.psi.PsiFile; 6 | // 7 | ///** 8 | // * @author idok 9 | // */ 10 | //public final class ScssLintConfigFileUtil { 11 | // private ScssLintConfigFileUtil() { 12 | // } 13 | // 14 | // public static boolean isScssLintConfigFile(PsiFile file) { 15 | // return file != null && (isScssLintConfigFile(file.getVirtualFile()) || file.getFileType().equals(ScssLintConfigFileType.INSTANCE)); 16 | // } 17 | // 18 | // public static boolean isScssLintConfigFile(PsiElement position) { 19 | // return isScssLintConfigFile(position.getContainingFile().getOriginalFile().getVirtualFile()); 20 | // } 21 | // 22 | // public static boolean isScssLintConfigFile(VirtualFile file) { 23 | // return file != null && file.getExtension() != null && 24 | // file.getExtension().equals(ScssLintConfigFileType.SCSS_LINT_YML); 25 | // } 26 | // 27 | //} 28 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/settings/ScssLintSettingsPage.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
146 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/settings/ScssLintSettingsPage.java: -------------------------------------------------------------------------------- 1 | package com.scss.settings; 2 | 3 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; 4 | import com.intellij.ide.actions.ShowSettingsUtilImpl; 5 | import com.intellij.openapi.application.ApplicationManager; 6 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; 7 | import com.intellij.openapi.options.Configurable; 8 | import com.intellij.openapi.options.ConfigurationException; 9 | import com.intellij.openapi.options.ex.SingleConfigurableEditor; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.psi.PsiManager; 12 | import com.intellij.ui.DocumentAdapter; 13 | import com.intellij.ui.HyperlinkLabel; 14 | import com.intellij.ui.TextFieldWithHistory; 15 | import com.intellij.ui.TextFieldWithHistoryWithBrowseButton; 16 | import com.intellij.util.ui.SwingHelper; 17 | import com.intellij.util.ui.UIUtil; 18 | import com.scss.ScssLintProjectComponent; 19 | import com.scss.utils.ScssLintFinder; 20 | import com.scss.utils.ScssLintRunner; 21 | import com.wix.settings.ValidationUtils; 22 | import com.wix.settings.Validator; 23 | import com.wix.ui.PackagesNotificationPanel; 24 | import com.wix.utils.FileUtils; 25 | import org.apache.commons.lang.StringUtils; 26 | import org.jetbrains.annotations.Nls; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import javax.swing.*; 31 | import javax.swing.event.DocumentEvent; 32 | import java.awt.*; 33 | import java.awt.event.ItemEvent; 34 | import java.io.File; 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | public class ScssLintSettingsPage implements Configurable { 39 | private static final String FIX_IT = "Fix it"; 40 | private static final String HOW_TO_USE_SCSS_LINT = "How to Use SCSS Lint"; 41 | private static final String HOW_TO_USE_LINK = "https://github.com/idok/scss-lint-plugin"; 42 | private final Project project; 43 | 44 | private JCheckBox pluginEnabledCheckbox; 45 | private JPanel panel; 46 | private JPanel errorPanel; 47 | private TextFieldWithHistoryWithBrowseButton scssLintConfigFile; 48 | private JRadioButton searchForConfigInRadioButton; 49 | private JRadioButton useSpecificConfigRadioButton; 50 | private HyperlinkLabel usageLink; 51 | private JLabel ScssLintConfigFilePathLabel; 52 | private JCheckBox treatAllIssuesCheckBox; 53 | private JLabel versionLabel; 54 | private JLabel scssLintExeLabel; 55 | private TextFieldWithHistoryWithBrowseButton scssLintExeField; 56 | private JCheckBox dismissConfigurationHints; 57 | private final PackagesNotificationPanel packagesNotificationPanel; 58 | 59 | public ScssLintSettingsPage(@NotNull final Project project) { 60 | this.project = project; 61 | configESLintBinField(); 62 | configScssLintConfigField(); 63 | this.packagesNotificationPanel = new PackagesNotificationPanel(project); 64 | errorPanel.add(this.packagesNotificationPanel.getComponent(), BorderLayout.CENTER); 65 | } 66 | 67 | private void addListeners() { 68 | useSpecificConfigRadioButton.addItemListener(e -> scssLintConfigFile.setEnabled(e.getStateChange() == ItemEvent.SELECTED)); 69 | pluginEnabledCheckbox.addItemListener(e -> { 70 | boolean enabled = e.getStateChange() == ItemEvent.SELECTED; 71 | setEnabledState(enabled); 72 | }); 73 | DocumentAdapter docAdp = new DocumentAdapter() { 74 | protected void textChanged(@NotNull DocumentEvent e) { 75 | updateLaterInEDT(); 76 | } 77 | }; 78 | scssLintExeField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); 79 | scssLintConfigFile.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); 80 | } 81 | 82 | private void updateLaterInEDT() { 83 | UIUtil.invokeLaterIfNeeded(ScssLintSettingsPage.this::update); 84 | } 85 | 86 | private void update() { 87 | ApplicationManager.getApplication().assertIsDispatchThread(); 88 | validate(); 89 | } 90 | 91 | private void setEnabledState(boolean enabled) { 92 | searchForConfigInRadioButton.setEnabled(enabled); 93 | useSpecificConfigRadioButton.setEnabled(enabled); 94 | scssLintConfigFile.setEnabled(enabled && useSpecificConfigRadioButton.isSelected()); 95 | scssLintExeField.setEnabled(enabled); 96 | ScssLintConfigFilePathLabel.setEnabled(enabled); 97 | scssLintExeLabel.setEnabled(enabled); 98 | treatAllIssuesCheckBox.setEnabled(enabled); 99 | } 100 | 101 | private void validate() { 102 | Validator validator = new Validator(); 103 | if (!ValidationUtils.validatePath(project, scssLintExeField.getChildComponent().getText(), false)) { 104 | validator.add(scssLintExeField.getChildComponent().getTextEditor(), "Path to Scss Lint exe is invalid {{LINK}}", FIX_IT); 105 | } 106 | if (!ValidationUtils.validatePath(project, scssLintConfigFile.getChildComponent().getText(), true)) { 107 | validator.add(scssLintConfigFile.getChildComponent().getTextEditor(), "Path to Scss Lint config is invalid {{LINK}}", FIX_IT); //Please correct path to 108 | } 109 | if (validator.hasErrors()) { 110 | versionLabel.setText("n.a."); 111 | } else { 112 | updateVersion(); 113 | } 114 | packagesNotificationPanel.processErrors(validator); 115 | } 116 | 117 | private ScssLintRunner.ScssLintSettings settings; 118 | 119 | private void updateVersion() { 120 | String scssExe = scssLintExeField.getChildComponent().getText(); 121 | if (settings != null && 122 | settings.scssLintExe.equals(scssExe) && 123 | settings.cwd.equals(project.getBasePath())) { 124 | return; 125 | } 126 | if (StringUtils.isEmpty(scssExe)) { 127 | return; 128 | } 129 | getVersion(scssExe, project.getBasePath()); 130 | } 131 | 132 | private void getVersion(String scssExe, String cwd) { 133 | if (StringUtils.isEmpty(scssExe)) { 134 | return; 135 | } 136 | settings = new ScssLintRunner.ScssLintSettings(); 137 | settings.scssLintExe = scssExe; 138 | settings.cwd = cwd; 139 | try { 140 | versionLabel.setText(ScssLintRunner.runVersion(settings)); 141 | } catch (Exception e) { 142 | versionLabel.setText("error"); 143 | e.printStackTrace(); 144 | } 145 | } 146 | 147 | private void configESLintBinField() { 148 | TextFieldWithHistory textFieldWithHistory = scssLintExeField.getChildComponent(); 149 | textFieldWithHistory.setHistorySize(-1); 150 | textFieldWithHistory.setMinimumAndPreferredWidth(0); 151 | 152 | SwingHelper.addHistoryOnExpansion(textFieldWithHistory, () -> { 153 | // File projectRoot = new File(project.getBaseDir().getPath()); 154 | List newFiles = ScssLintFinder.findAllScssLintExe(); //searchForESLintBin(projectRoot); 155 | return FileUtils.toAbsolutePath(newFiles); 156 | }); 157 | 158 | SwingHelper.installFileCompletionAndBrowseDialog(project, scssLintExeField, "Select SCSS Lint Exe", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); 159 | } 160 | 161 | private void configScssLintConfigField() { 162 | TextFieldWithHistory textFieldWithHistory = scssLintConfigFile.getChildComponent(); 163 | textFieldWithHistory.setHistorySize(-1); 164 | textFieldWithHistory.setMinimumAndPreferredWidth(0); 165 | 166 | SwingHelper.addHistoryOnExpansion(textFieldWithHistory, () -> { 167 | String f = project.getBasePath(); 168 | if (f != null) { 169 | File projectRoot = new File(f); 170 | return ScssLintFinder.searchForLintConfigFiles(projectRoot); 171 | } 172 | return new ArrayList<>(); 173 | }); 174 | 175 | SwingHelper.installFileCompletionAndBrowseDialog(project, scssLintConfigFile, "Select SCSS Lint Config", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); 176 | } 177 | 178 | @Nls 179 | @Override 180 | public String getDisplayName() { 181 | return "SCSS Lint"; 182 | } 183 | 184 | @Nullable 185 | @Override 186 | public String getHelpTopic() { 187 | return null; 188 | } 189 | 190 | @Nullable 191 | @Override 192 | public JComponent createComponent() { 193 | loadSettings(); 194 | getVersion(scssLintExeField.getChildComponent().getText(), project.getBasePath()); 195 | addListeners(); 196 | return panel; 197 | } 198 | 199 | @Override 200 | public boolean isModified() { 201 | return pluginEnabledCheckbox.isSelected() != getSettings().pluginEnabled 202 | || dismissConfigurationHints.isSelected() != getSettings().dismissConfigurationHints 203 | || !scssLintExeField.getChildComponent().getText().equals(getSettings().scssLintExecutable) 204 | || treatAllIssuesCheckBox.isSelected() != getSettings().treatAllIssuesAsWarnings 205 | || !getLintConfigFile().equals(getSettings().scssLintConfigFile); 206 | } 207 | 208 | private String getLintConfigFile() { 209 | return useSpecificConfigRadioButton.isSelected() ? scssLintConfigFile.getChildComponent().getText() : ""; 210 | } 211 | 212 | @Override 213 | public void apply() throws ConfigurationException { 214 | saveSettings(); 215 | PsiManager.getInstance(project).dropResolveCaches(); 216 | } 217 | 218 | private void saveSettings() { 219 | Settings settings = getSettings(); 220 | settings.pluginEnabled = pluginEnabledCheckbox.isSelected(); 221 | settings.scssLintExecutable = scssLintExeField.getChildComponent().getText(); 222 | settings.scssLintConfigFile = getLintConfigFile(); 223 | settings.treatAllIssuesAsWarnings = treatAllIssuesCheckBox.isSelected(); 224 | settings.dismissConfigurationHints = dismissConfigurationHints.isSelected(); 225 | project.getComponent(ScssLintProjectComponent.class).validateSettings(); 226 | DaemonCodeAnalyzer.getInstance(project).restart(); 227 | } 228 | 229 | private void loadSettings() { 230 | Settings settings = getSettings(); 231 | pluginEnabledCheckbox.setSelected(settings.pluginEnabled); 232 | scssLintExeField.getChildComponent().setText(settings.scssLintExecutable); 233 | scssLintConfigFile.getChildComponent().setText(settings.scssLintConfigFile); 234 | 235 | boolean hasConfig = StringUtils.isNotEmpty(settings.scssLintConfigFile); 236 | searchForConfigInRadioButton.setSelected(!hasConfig); 237 | useSpecificConfigRadioButton.setSelected(hasConfig); 238 | scssLintConfigFile.setEnabled(hasConfig); 239 | treatAllIssuesCheckBox.setSelected(settings.treatAllIssuesAsWarnings); 240 | dismissConfigurationHints.setSelected(settings.dismissConfigurationHints); 241 | setEnabledState(settings.pluginEnabled); 242 | } 243 | 244 | @Override 245 | public void reset() { 246 | loadSettings(); 247 | } 248 | 249 | @Override 250 | public void disposeUIResources() { 251 | } 252 | 253 | protected Settings getSettings() { 254 | return Settings.getInstance(project); 255 | } 256 | 257 | private void createUIComponents() { 258 | // TODO: place custom component creation code here 259 | usageLink = SwingHelper.createWebHyperlink(HOW_TO_USE_SCSS_LINT, HOW_TO_USE_LINK); 260 | } 261 | 262 | public void showSettings() { 263 | String dimensionKey = ShowSettingsUtilImpl.createDimensionKey(this); 264 | SingleConfigurableEditor singleConfigurableEditor = new SingleConfigurableEditor(project, this, dimensionKey, false); 265 | singleConfigurableEditor.show(); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/settings/Settings.java: -------------------------------------------------------------------------------- 1 | package com.scss.settings; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.util.xmlb.XmlSerializerUtil; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | @State( 10 | name = "ScssLintProjectComponent", 11 | storages = @Storage("scssLintPlugin.xml") 12 | ) 13 | public class Settings implements PersistentStateComponent { 14 | public String scssLintConfigFile = ""; 15 | public String scssLintExecutable = ""; 16 | public boolean treatAllIssuesAsWarnings; 17 | public boolean pluginEnabled; 18 | public boolean dismissConfigurationHints; 19 | 20 | public static Settings getInstance(Project project) { 21 | return ServiceManager.getService(project, Settings.class); 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public Settings getState() { 27 | return this; 28 | } 29 | 30 | @Override 31 | public void loadState(@NotNull Settings state) { 32 | XmlSerializerUtil.copyBean(state, this); 33 | } 34 | 35 | public String getVersion() { 36 | return scssLintExecutable + scssLintConfigFile + treatAllIssuesAsWarnings; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/utils/ScssLintFinder.java: -------------------------------------------------------------------------------- 1 | package com.scss.utils; 2 | 3 | import com.intellij.execution.configurations.PathEnvironmentVariableUtil; 4 | import com.intellij.util.containers.ContainerUtil; 5 | import com.wix.nodejs.NodeFinder; 6 | import com.wix.utils.FileUtils; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.io.File; 10 | import java.io.FilenameFilter; 11 | import java.util.LinkedHashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | public final class ScssLintFinder { 16 | public static final String SCSS_LINT_BASE_NAME = NodeFinder.getBinName("scss-lint"); 17 | public static final String SCSS_LINT_YML = "scss-lint.yml"; 18 | 19 | private ScssLintFinder() { 20 | } 21 | 22 | @NotNull 23 | public static List findAllScssLintExe() { 24 | // TODO looks like on windows it only searches system path and not user's 25 | List fromPath = PathEnvironmentVariableUtil.findAllExeFilesInPath(SCSS_LINT_BASE_NAME); 26 | Set exes = new LinkedHashSet<>(fromPath); 27 | return ContainerUtil.newArrayList(exes); 28 | } 29 | 30 | /** 31 | * find possible scss-lint config files 32 | * @param projectRoot project root 33 | * @return a list of scss-lint config files 34 | */ 35 | public static List searchForLintConfigFiles(final File projectRoot) { 36 | FilenameFilter filter = (file, name) -> name.equals(SCSS_LINT_YML); 37 | List files = FileUtils.recursiveVisitor(projectRoot, filter); 38 | return ContainerUtil.map(files, curFile -> FileUtils.makeRelative(projectRoot, new File(curFile))); 39 | } 40 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/utils/ScssLintRunner.java: -------------------------------------------------------------------------------- 1 | package com.scss.utils; 2 | 3 | import com.intellij.execution.ExecutionException; 4 | import com.intellij.execution.configurations.GeneralCommandLine; 5 | import com.intellij.execution.process.ProcessOutput; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.scss.utils.scssLint.Lint; 8 | import com.scss.utils.scssLint.LintResult; 9 | import com.wix.nodejs.NodeRunner; 10 | import org.apache.commons.lang.StringUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.io.File; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public final class ScssLintRunner { 18 | private ScssLintRunner() { 19 | } 20 | 21 | private static final Logger LOG = Logger.getInstance(ScssLintRunner.class); 22 | 23 | private static final int TIME_OUT = (int) TimeUnit.SECONDS.toMillis(120L); 24 | /** 25 | * One or more files specified were not found 26 | */ 27 | private static final int FILES_NOT_FOUND = 66; 28 | 29 | public static class ScssLintSettings { 30 | public ScssLintSettings() { 31 | } 32 | 33 | public ScssLintSettings(String config, String cwd, String targetFile, String scssLintExe) { 34 | this.config = config; 35 | this.cwd = cwd; 36 | this.targetFile = targetFile; 37 | this.scssLintExe = scssLintExe; 38 | } 39 | 40 | public String config; 41 | public String cwd; 42 | public String targetFile; 43 | public String scssLintExe; 44 | } 45 | 46 | public static ScssLintSettings buildSettings(@NotNull String cwd, @NotNull String path, @NotNull String scssLintExe, @Nullable String config) { 47 | return new ScssLintSettings(config, cwd, path, scssLintExe); 48 | } 49 | 50 | public static LintResult runLint(@NotNull String cwd, @NotNull String file, @NotNull String scssLintExe, @Nullable String config) { 51 | LintResult result = new LintResult(); 52 | try { 53 | ProcessOutput out = lint(cwd, file, scssLintExe, config); 54 | // if (out.getExitCode() == 0) { 55 | // } else { 56 | result.errorOutput = out.getStderr(); 57 | try { 58 | if (out.getExitCode() != FILES_NOT_FOUND) { 59 | result.lint = Lint.parse(out.getStdout()); 60 | } 61 | } catch (Exception e) { 62 | result.errorOutput = out.getStdout(); 63 | } 64 | // } 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | result.errorOutput = e.toString(); 68 | } 69 | return result; 70 | } 71 | 72 | @NotNull 73 | public static ProcessOutput lint(@NotNull String cwd, @NotNull String file, @NotNull String scssLintExe, @Nullable String config) throws ExecutionException { 74 | //scss-lint one.scss -f XML 75 | GeneralCommandLine commandLine = new GeneralCommandLine(); 76 | commandLine.setWorkDirectory(cwd); 77 | // if (SystemInfo.isWindows) { 78 | // commandLine.setExePath(settings.eslintExecutablePath); 79 | // } else { 80 | // commandLine.setExePath(settings.node); 81 | // commandLine.addParameter(settings.eslintExecutablePath); 82 | // } 83 | commandLine.setExePath(scssLintExe); 84 | // GeneralCommandLine commandLine = createCommandLine(buildSettings(cwd, file, scssLintExe, config)); 85 | commandLine.addParameter(file); 86 | commandLine.addParameter("-f"); 87 | commandLine.addParameter("JSON"); 88 | // commandLine.addParameter("XML"); 89 | if (StringUtils.isNotEmpty(config)) { 90 | commandLine.addParameter("-c"); 91 | commandLine.addParameter(config); 92 | } 93 | return NodeRunner.execute(commandLine, TIME_OUT); 94 | } 95 | 96 | @NotNull 97 | private static ProcessOutput version(@NotNull ScssLintSettings settings) throws ExecutionException { 98 | GeneralCommandLine commandLine = createCommandLine(settings); 99 | commandLine.addParameter("-v"); 100 | return NodeRunner.execute(commandLine, TIME_OUT); 101 | } 102 | 103 | @NotNull 104 | public static String runVersion(@NotNull ScssLintSettings settings) throws ExecutionException { 105 | if (!new File(settings.scssLintExe).exists()) { 106 | LOG.warn("Calling version with invalid scssLintExe exe " + settings.scssLintExe); 107 | return ""; 108 | } 109 | ProcessOutput out = version(settings); 110 | if (out.getExitCode() == 0) { 111 | return out.getStdout().trim(); 112 | } 113 | return ""; 114 | } 115 | 116 | @NotNull 117 | private static GeneralCommandLine createCommandLine(@NotNull ScssLintSettings settings) { 118 | GeneralCommandLine commandLine = new GeneralCommandLine(); 119 | commandLine.setWorkDirectory(settings.cwd); 120 | commandLine.setExePath(settings.scssLintExe); 121 | return commandLine; 122 | } 123 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/utils/scssLint/Lint.java: -------------------------------------------------------------------------------- 1 | package com.scss.utils.scssLint; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | 7 | import java.lang.reflect.Type; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class Lint { 12 | // public File file; 13 | // 14 | // public static Lint read(String xml) { 15 | // XStream xstream = new XStream(); 16 | // xstream.alias("lint", Lint.class); 17 | // xstream.alias("file", File.class); 18 | // xstream.alias("issue", Issue.class); 19 | // xstream.addImplicitCollection(File.class, "issues"); 20 | // xstream.useAttributeFor(File.class, "name"); 21 | // xstream.useAttributeFor(Issue.class, "linter"); 22 | // xstream.useAttributeFor(Issue.class, "line"); 23 | // xstream.useAttributeFor(Issue.class, "column"); 24 | // xstream.useAttributeFor(Issue.class, "length"); 25 | // xstream.useAttributeFor(Issue.class, "severity"); 26 | // xstream.useAttributeFor(Issue.class, "reason"); 27 | // return (Lint) xstream.fromXML(xml); 28 | // } 29 | // 30 | // public static class File { 31 | // public String name; 32 | // public List issues = new ArrayList(); 33 | // } 34 | 35 | public static Map> parse(String json) { 36 | GsonBuilder builder = new GsonBuilder(); 37 | // builder.registerTypeAdapterFactory(adapter); 38 | Gson g = builder.setPrettyPrinting().create(); 39 | Type listType = new TypeToken>>() {}.getType(); 40 | return g.fromJson(json, listType); 41 | } 42 | 43 | public static class Issue { 44 | public String linter; 45 | public int line; 46 | public int column; 47 | public int length; 48 | public String severity; 49 | public String reason; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/utils/scssLint/LintResult.java: -------------------------------------------------------------------------------- 1 | package com.scss.utils.scssLint; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class LintResult { 7 | public Map> lint; 8 | public String errorOutput; 9 | } 10 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/java/com/scss/utils/scssLint/Outdated.java: -------------------------------------------------------------------------------- 1 | //package com.scss.utils.scssLint; 2 | // 3 | //import com.google.gson.Gson; 4 | //import com.google.gson.GsonBuilder; 5 | //import com.google.gson.annotations.SerializedName; 6 | //import com.google.gson.reflect.TypeToken; 7 | // 8 | //import java.lang.reflect.Type; 9 | //import java.util.List; 10 | //import java.util.Map; 11 | // 12 | //public class Outdated { 13 | // @SerializedName("react-templates") 14 | // public OutdatedClass rt; 15 | // 16 | // public static Outdated parseNpmOutdated(String json) { 17 | // GsonBuilder builder = new GsonBuilder(); 18 | //// builder.registerTypeAdapterFactory(adapter); 19 | // Gson g = builder.setPrettyPrinting().create(); 20 | // Type listType = new TypeToken() {}.getType(); 21 | // return g.fromJson(json, listType); 22 | // } 23 | // 24 | // public static class OutdatedClass { 25 | // public String current; 26 | // public String wanted; 27 | // public String latest; 28 | // public String location; 29 | // } 30 | // 31 | // final static String json = "{" + 32 | // "\"scss-lint-plugin/testData/one.scss\": [" + 33 | // "{\"line\": 5,\"column\": 3,\"length\": 22,\"severity\": \"warning\",\"reason\": \"Properties should be ordered color, font\",\"linter\": \"PropertySortOrder\"}" + 34 | // "]" + 35 | // "}"; 36 | // 37 | // public static class Koko { 38 | //// @SerializedName("files") 39 | //// public List> files; 40 | // public Map> files; 41 | // } 42 | // 43 | // public static class Obj { 44 | // public String line; 45 | // public int column; 46 | // public int length; 47 | // public String severity; 48 | // public String reason; 49 | // public String linter; 50 | // } 51 | // 52 | // public static Map> parseNpmOutdated2(String json) { 53 | // GsonBuilder builder = new GsonBuilder(); 54 | //// builder.registerTypeAdapterFactory(adapter); 55 | // Gson g = builder.setPrettyPrinting().create(); 56 | // Type listType = new TypeToken< Map>>() {}.getType(); 57 | // return g.fromJson(json, listType); 58 | // } 59 | // 60 | // 61 | // public static void main(String[] args) { 62 | // Map> koko = parseNpmOutdated2(json); 63 | // System.out.println(koko); 64 | // } 65 | // 66 | // 67 | //} 68 | // 69 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.wix.scss.lint 3 | scss-lint 4 | 2.1.0 5 | Ido 6 | HTML/CSS Development 7 | 9 |

Support displaying SCSS Lint warnings as intellij inspections

10 | ]]>
11 | 12 | 2.1.1 Bug fixes

14 |

2.1.0 Allow to dismiss configuration setting notice

15 |

2.0.1 Fix compatibility issue with Idea 2018.1

16 |

2.0.0 Fix compatibility issue with Idea 2018

17 |

1.1.2 Bug fixes

18 |

1.1.1 Bug fixes

19 |

1.1.0 Idea 145 compatibility

20 |

1.0.17 Bug fixes

21 |

1.0.16 Bug fixes

22 |

1.0.15 Bug fixes

23 |

1.0.14 Bug fixes

24 |

1.0.13 Fix compatibility issue with scss-lint 0.39.0

25 |

1.0.12 Bug fixes

26 |

1.0.11 Remove YAML dependency

27 |

1.0.10 Fix PhpStorm compatibility issue

28 |

1.0.9 Fix WebStorm 9 compatibility issue

29 |

1.0.8 Add quick fix to sort properties

30 |

1.0.7 Fixed an NPE bug.

31 |

1.0.6 Window path issue and other bug fixes.

32 |

1.0.5 Bug fixes.

33 |

1.0.4 Bug fixes.

34 |

1.0.3 Fix execution issue on windows.

35 |

1.0.2 Bug fixes.

36 |

1.0.1 First version.

37 | ]]>
38 | 39 | com.intellij.modules.lang 40 | 41 | 42 | com.intellij.css 43 | 44 | 45 | 46 | 47 | 48 | 49 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | com.scss.ScssLintProjectComponent 78 | 79 | 80 | 81 | 82 | 83 | 84 |
85 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/resources/com/scss/ScssLintBundle.properties: -------------------------------------------------------------------------------- 1 | #Inspection suppression 2 | unused.property.suppress.for.statement=Suppress for statement 3 | unused.property.suppress.for.block=Suppress for block 4 | unused.property.suppress.for.file=Suppress for file 5 | 6 | #Inspections 7 | scss.inspections.group.name=Sass/SCSS 8 | scss.inspection.group.name=Code quality tools 9 | scss.inspection.undefined.step.name=Undefined step 10 | scss.inspection.undefined.step.msg.name=Undefined step reference: 11 | scss.property.inspection.display.name=SCSS 12 | scss.property.inspection.message=SCSS-LINT: {0} ({1}) 13 | 14 | scss.rules.dir.does.not.exist=Rules directory not found. Path {0} not found in project 15 | scss.rules.dir.is.not.a.dir=Rules path is not a directory. Path {0} not found in project 16 | 17 | scss.directory.does.not.exist={0} directory not found. Path {1} not found in project 18 | scss.directory.is.not.a.dir={0} path is not a directory. Path {1} not found in project 19 | scss.path.is.empty=Path to {0} is empty 20 | scss.file.does.not.exist={0} file not found. Path {1} not found in project 21 | scss.file.is.not.a.file={0} path is not a file. Path {1} not found in project 22 | 23 | inspection.fix.sort=Sort properties 24 | 25 | properties.files.inspection.group.display.name=SCSS 26 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/main/resources/inspectionDescriptions/ScssLintInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Runs SCSS Lint validator for specified scss file. 4 | 5 | 6 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/ScssLintTest.java: -------------------------------------------------------------------------------- 1 | //package com.scsslint; 2 | // 3 | //import com.intellij.openapi.project.Project; 4 | //import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; 5 | //import com.scss.ScssLintExternalAnnotator; 6 | //import com.scss.ScssLintInspection; 7 | //import com.scss.settings.Settings; 8 | ////import org.jetbrains.plugins.scss.SCSSFileType; 9 | // 10 | //public class ScssLintTest extends LightPlatformCodeInsightFixtureTestCase { 11 | // @Override 12 | // protected String getTestDataPath() { 13 | // return TestUtils.getTestDataPath(); 14 | // } 15 | // 16 | // @Override 17 | // protected void setUp() throws Exception { 18 | // super.setUp(); 19 | // } 20 | // 21 | // @Override 22 | // protected boolean isWriteActionRequired() { 23 | // return false; 24 | // } 25 | // 26 | // private void doTest(final String file) { 27 | // Project project = myFixture.getProject(); 28 | // Settings settings = Settings.getInstance(project); 29 | //// settings.scssLintExecutable = ScssLintRunnerTest.SCSS_LINT_BIN; 30 | //// settings.scssLintConfigFile = getTestDataPath() + "/.eslintrc"; 31 | //// settings.nodeInterpreter = ScssLintRunnerTest.SCSS_EXE; 32 | //// settings.rulesPath = ""; 33 | // settings.pluginEnabled = true; 34 | // myFixture.configureByFile(file); 35 | // myFixture.enableInspections(new ScssLintInspection()); 36 | // myFixture.checkHighlighting(true, false, true); 37 | // } 38 | // 39 | // private void doTest() { 40 | // String name = getTestName(false).replaceAll("_", "-"); 41 | // doTest("/inspections/" + name + '.' + ScssLintExternalAnnotator.SCSS); 42 | // } 43 | // 44 | // public void testCapitalizationInSelector() { 45 | // doTest(); 46 | // } 47 | // 48 | // public void testEmptyRule() { 49 | // doTest(); 50 | // } 51 | // 52 | // public void testHexLength() { 53 | // doTest(); 54 | // } 55 | //} 56 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.scsslint; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | 5 | import java.io.File; 6 | import java.net.URISyntaxException; 7 | import java.net.URL; 8 | 9 | /** 10 | * @author idok 11 | */ 12 | public final class TestUtils { 13 | 14 | private TestUtils() { 15 | } 16 | 17 | private static final Logger LOG = Logger.getInstance(TestUtils.class); 18 | 19 | private static String TEST_DATA_PATH; 20 | 21 | public static String getTestDataPath() { 22 | if (TEST_DATA_PATH == null) { 23 | ClassLoader loader = TestUtils.class.getClassLoader(); 24 | URL resource = loader.getResource("testData"); 25 | try { 26 | TEST_DATA_PATH = new File("testData").getAbsolutePath(); 27 | if (resource != null) { 28 | TEST_DATA_PATH = new File(resource.toURI()).getPath().replace(File.separatorChar, '/'); 29 | } 30 | } catch (URISyntaxException e) { 31 | LOG.error(e); 32 | return null; 33 | } 34 | } 35 | return TEST_DATA_PATH; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/TestUtils2.java: -------------------------------------------------------------------------------- 1 | package com.scsslint; 2 | 3 | import com.intellij.openapi.application.PathManager; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.roots.ModifiableRootModel; 6 | import com.intellij.openapi.roots.ModuleRootManager; 7 | import com.intellij.openapi.roots.OrderEnumerator; 8 | import com.intellij.openapi.roots.OrderRootType; 9 | import com.intellij.openapi.roots.libraries.Library; 10 | import com.intellij.openapi.roots.libraries.LibraryTable; 11 | import com.intellij.openapi.util.io.FileUtil; 12 | import com.intellij.openapi.vfs.VfsUtil; 13 | import com.intellij.util.Processor; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.io.File; 18 | import java.net.URISyntaxException; 19 | import java.net.URL; 20 | import java.util.List; 21 | 22 | /** 23 | * @author ilyas 24 | */ 25 | public final class TestUtils2 { 26 | 27 | public static final String BASE_TEST_DATA_PATH = findTestDataPath(); 28 | public static final String SDK_HOME_PATH = BASE_TEST_DATA_PATH + "/sdk"; 29 | 30 | private TestUtils2() { 31 | } 32 | 33 | private static String findTestDataPath() { 34 | final File f = new File("/Users/idok/Projects/eslint-plugin/testData"); // launched from 'Dart-plugin' project 35 | if (f.isDirectory()) return FileUtil.toSystemIndependentName(f.getAbsolutePath()); 36 | 37 | // PathManager.get 38 | return FileUtil.toSystemIndependentName(PathManager.getHomePath() + "/testData"); 39 | } 40 | 41 | private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.clojure.util.TestUtils"); 42 | 43 | private static final String[] RUN_PATHS = { 44 | "out/test/clojure-plugin", 45 | // if tests are run using ant script 46 | "dist/testClasses"}; 47 | 48 | private static String TEST_DATA_PATH; 49 | 50 | public static final String CARET_MARKER = ""; 51 | public static final String BEGIN_MARKER = ""; 52 | public static final String END_MARKER = ""; 53 | 54 | public static String getTestDataPath() { 55 | if (TEST_DATA_PATH == null) { 56 | ClassLoader loader = TestUtils2.class.getClassLoader(); 57 | URL resource = loader.getResource("testData"); 58 | try { 59 | TEST_DATA_PATH = new File("testData").getAbsolutePath(); 60 | if (resource != null) { 61 | TEST_DATA_PATH = new File(resource.toURI()).getPath().replace(File.separatorChar, '/'); 62 | } 63 | } catch (URISyntaxException e) { 64 | LOG.error(e); 65 | return null; 66 | } 67 | } 68 | return TEST_DATA_PATH; 69 | } 70 | 71 | public static String getMockJdk() { 72 | return getTestDataPath() + "/mockJDK"; 73 | } 74 | 75 | 76 | public static String getMockClojureLib() { 77 | return getTestDataPath() + "/mockClojureLib/clojure-1.5.jar"; 78 | } 79 | 80 | public static String getMockClojureContribLib() { 81 | return getTestDataPath() + "/mockClojureLib/clojure-contrib.jar"; 82 | } 83 | 84 | @Nullable 85 | public static String getDataPath(@NotNull Class clazz) { 86 | final String classDir = getClassRelativePath(clazz); 87 | String moduleDir = getModulePath(clazz); 88 | return classDir != null && moduleDir != null ? moduleDir + "/" + classDir + "/data/" : null; 89 | } 90 | 91 | public static String getOutputPath(final Class clazz) { 92 | final String classDir = getClassRelativePath(clazz); 93 | String moduleDir = getModulePath(clazz); 94 | return classDir != null && moduleDir != null ? moduleDir + "/" + classDir + "/output/" : null; 95 | } 96 | 97 | @Nullable 98 | public static String getDataPath(@NotNull Class s, @NotNull final String relativePath) { 99 | return getDataPath(s) + "/" + relativePath; 100 | } 101 | 102 | @Nullable 103 | public static String getClassRelativePath(@NotNull Class s) { 104 | String classFullPath = getClassFullPath(s); 105 | for (String path : RUN_PATHS) { 106 | final String dataPath = getClassDirPath(classFullPath, path); 107 | if (dataPath != null) { 108 | return dataPath; 109 | } 110 | } 111 | return null; 112 | } 113 | 114 | @Nullable 115 | public static String getModulePath(@NotNull Class s) { 116 | String classFullPath = getClassFullPath(s); 117 | for (String path : RUN_PATHS) { 118 | final String dataPath = getModulePath(classFullPath, path); 119 | if (dataPath != null) { 120 | return dataPath; 121 | } 122 | } 123 | return null; 124 | } 125 | 126 | public static String getClassFullPath(@NotNull final Class s) { 127 | String name = s.getSimpleName() + ".class"; 128 | final URL url = s.getResource(name); 129 | return url.getPath(); 130 | } 131 | 132 | @Nullable 133 | private static String getModulePath(@NotNull String s, @NotNull final String indicator) { 134 | int n = s.indexOf(indicator); 135 | if (n == -1) { 136 | return null; 137 | } 138 | return s.substring(0, n - 1); 139 | } 140 | 141 | @Nullable 142 | private static String getClassDirPath(@NotNull String s, @NotNull final String indicator) { 143 | int n = s.indexOf(indicator); 144 | if (n == -1) { 145 | return null; 146 | } 147 | s = "test" + s.substring(n + indicator.length()); 148 | s = s.substring(0, s.lastIndexOf('/')); 149 | return s; 150 | } 151 | 152 | public static ModifiableRootModel addLibrary(ModifiableRootModel rootModel, 153 | ModuleRootManager rootManager, OrderEnumerator libs, 154 | List libModels, 155 | final String clojureLibraryName, String mockLib, String mockLibSrc) { 156 | class CustomProcessor implements Processor { 157 | private boolean result = true; 158 | 159 | public boolean process(Library library) { 160 | boolean res = library.getName().equals(clojureLibraryName); 161 | if (res) result = false; 162 | return result; 163 | } 164 | } 165 | CustomProcessor processor = new CustomProcessor(); 166 | libs.forEachLibrary(processor); 167 | if (processor.result) { 168 | if (rootModel == null) { 169 | rootModel = rootManager.getModifiableModel(); 170 | } 171 | final LibraryTable libraryTable = rootModel.getModuleLibraryTable(); 172 | final Library scalaLib = libraryTable.createLibrary(clojureLibraryName); 173 | final Library.ModifiableModel libModel = scalaLib.getModifiableModel(); 174 | libModels.add(libModel); 175 | addLibraryRoots(libModel, mockLib, mockLibSrc); 176 | } 177 | return rootModel; 178 | } 179 | 180 | public static void addLibraryRoots(Library.ModifiableModel libModel, String mockLib, String mockLibSrc) { 181 | final File libRoot = new File(mockLib); 182 | assert libRoot.exists(); 183 | 184 | libModel.addRoot(VfsUtil.getUrlForLibraryRoot(libRoot), OrderRootType.CLASSES); 185 | if (mockLibSrc != null) { 186 | final File srcRoot = new File(mockLibSrc); 187 | assert srcRoot.exists(); 188 | libModel.addRoot(VfsUtil.getUrlForLibraryRoot(srcRoot), OrderRootType.SOURCES); 189 | } 190 | // ((VirtualFilePointerManagerImpl) VirtualFilePointerManager.getInstance()).storePointers(); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/utils/ConfigFinderTest.java: -------------------------------------------------------------------------------- 1 | //package com.scsslint.utils; 2 | // 3 | //import com.scss.utils.ConfigFinder; 4 | //import com.scss.utils.ScssLintRunner; 5 | //import org.junit.Test; 6 | //import static com.scsslint.utils.Settings.*; 7 | // 8 | //public class ConfigFinderTest { 9 | // public static final String CONFIG = ""; 10 | // 11 | // private static ScssLintRunner.ScssLintSettings createSettings(String targetFile) { 12 | // return ScssLintRunner.buildSettings(PLUGIN_ROOT, targetFile, SCSS_EXE, CONFIG); 13 | // } 14 | // 15 | // private static ScssLintRunner.ScssLintSettings createSettings() { 16 | // return createSettings(""); 17 | // } 18 | // 19 | // @Test 20 | // public void testMultiply() { 21 | // ConfigFinder.INSTANCE.readConfig("~/Projects/packages-plugin-test", "/Users/idok/Projects/packages-plugin-test/scss/.scss-lint.yml"); 22 | // } 23 | //} 24 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/utils/PrintAppDataDir.java: -------------------------------------------------------------------------------- 1 | //package com.scsslint.utils; 2 | // 3 | //import com.sun.jna.Library; 4 | //import com.sun.jna.Native; 5 | //import com.sun.jna.NativeMapped; 6 | //import com.sun.jna.PointerType; 7 | //import com.sun.jna.win32.W32APIFunctionMapper; 8 | //import com.sun.jna.win32.W32APITypeMapper; 9 | // 10 | //import java.util.HashMap; 11 | //import java.util.Map; 12 | // 13 | //public class PrintAppDataDir { 14 | // 15 | // public static void main(String[] args) { 16 | // if (com.sun.jna.Platform.isWindows()) { 17 | // HWND hwndOwner = null; 18 | // int nFolder = Shell32.CSIDL_LOCAL_APPDATA; 19 | // HANDLE hToken = null; 20 | // int dwFlags = Shell32.SHGFP_TYPE_CURRENT; 21 | // char[] pszPath = new char[Shell32.MAX_PATH]; 22 | // int hResult = Shell32.INSTANCE.SHGetFolderPath(hwndOwner, nFolder, hToken, dwFlags, pszPath); 23 | // if (Shell32.S_OK == hResult) { 24 | // String path = new String(pszPath); 25 | // int len = path.indexOf('\0'); 26 | // path = path.substring(0, len); 27 | // System.out.println(path); 28 | // } else { 29 | // System.err.println("Error: " + hResult); 30 | // } 31 | // } 32 | // } 33 | // 34 | // private static Map OPTIONS = new HashMap(); 35 | // 36 | // static { 37 | // OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); 38 | // OPTIONS.put(Library.OPTION_FUNCTION_MAPPER, 39 | // W32APIFunctionMapper.UNICODE); 40 | // } 41 | // 42 | // static class HANDLE extends PointerType implements NativeMapped { 43 | // } 44 | // 45 | // static class HWND extends HANDLE { 46 | // } 47 | // 48 | // static interface Shell32 extends Library { 49 | // 50 | // public static final int MAX_PATH = 260; 51 | // public static final int CSIDL_LOCAL_APPDATA = 0x001c; 52 | // public static final int SHGFP_TYPE_CURRENT = 0; 53 | // public static final int SHGFP_TYPE_DEFAULT = 1; 54 | // public static final int S_OK = 0; 55 | // 56 | // static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class, OPTIONS); 57 | // 58 | // /** 59 | // * see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx 60 | // *

61 | // * HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, 62 | // * DWORD dwFlags, LPTSTR pszPath); 63 | // */ 64 | // public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken, int dwFlags, char[] pszPath); 65 | // } 66 | //} -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/utils/ScssLintRunner2Test.java: -------------------------------------------------------------------------------- 1 | //package com.scsslint.utils; 2 | // 3 | //import com.intellij.execution.ExecutionException; 4 | //import com.intellij.execution.configurations.PathEnvironmentVariableUtil; 5 | //import com.scss.utils.ScssLintFinder; 6 | //import com.scss.utils.ScssLintRunner; 7 | //import com.scss.utils.scssLint.LintResult; 8 | //import org.junit.Test; 9 | // 10 | //import java.io.File; 11 | //import java.util.List; 12 | // 13 | //import static org.junit.Assert.assertEquals; 14 | //import static com.scsslint.utils.Settings.*; 15 | // 16 | //public class ScssLintRunner2Test { 17 | // private static final String CONFIG = ""; 18 | // 19 | // private static ScssLintRunner.ScssLintSettings createSettings(String targetFile) { 20 | // return ScssLintRunner.buildSettings(PLUGIN_ROOT, targetFile, SCSS_EXE, CONFIG); 21 | // } 22 | // 23 | // private static ScssLintRunner.ScssLintSettings createSettings() { 24 | // return createSettings(""); 25 | // } 26 | // 27 | // @Test 28 | // public void testLint() { 29 | // String scssFile = "testData/one.scss"; 30 | // ScssLintRunner.ScssLintSettings settings = createSettings(scssFile); 31 | // LintResult result = ScssLintRunner.runLint(settings.cwd, settings.targetFile, SCSS_EXE, CONFIG); 32 | // assertEquals("should have 1 issue", 1, result.lint.get(scssFile).size()); 33 | // assertEquals("should have props warn", "Properties should be ordered color, font", result.lint.get(scssFile).get(0).reason); 34 | // } 35 | // 36 | // @Test 37 | // public void findExeInPath() { 38 | // List fromPath = PathEnvironmentVariableUtil.findAllExeFilesInPath(ScssLintFinder.SCSS_LINT_BASE_NAME); 39 | // System.out.println(fromPath); 40 | // assertEquals("should find exe", "/usr/local/bin/scss-lint", fromPath.get(0).toString()); 41 | // } 42 | // 43 | // @Test 44 | // public void testVersion() { 45 | // ScssLintRunner.ScssLintSettings settings = createSettings(); 46 | // try { 47 | // String version = ScssLintRunner.runVersion(settings); 48 | // assertEquals("version should be", "scss-lint 0.38.0", version); 49 | // } catch (ExecutionException e) { 50 | // e.printStackTrace(); 51 | // } 52 | // } 53 | //} 54 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/utils/ScssLintRunnerTest.java: -------------------------------------------------------------------------------- 1 | package com.scsslint.utils; 2 | 3 | import com.intellij.execution.configurations.PathEnvironmentVariableUtil; 4 | import com.scss.utils.ScssLintFinder; 5 | import com.scss.utils.ScssLintRunner; 6 | import org.junit.Test; 7 | 8 | import java.io.File; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import static com.scsslint.utils.Settings.PLUGIN_ROOT; 13 | import static com.scsslint.utils.Settings.SCSS_EXE; 14 | import static org.junit.Assert.assertArrayEquals; 15 | import static org.junit.Assert.assertEquals; 16 | 17 | public class ScssLintRunnerTest { 18 | public static final String CONFIG = ""; 19 | 20 | private static ScssLintRunner.ScssLintSettings createSettings(String targetFile) { 21 | return ScssLintRunner.buildSettings(PLUGIN_ROOT, targetFile, SCSS_EXE, CONFIG); 22 | } 23 | 24 | private static ScssLintRunner.ScssLintSettings createSettings() { 25 | return createSettings(""); 26 | } 27 | 28 | // @Test 29 | // public void testMultiply() { 30 | // String scssFile = "testData/one.scss"; 31 | //// String scssFile = PLUGIN_ROOT + "/testData/one.scss"; 32 | // ScssLintRunner.ScssLintSettings settings = createSettings(scssFile); 33 | // LintResult result = ScssLintRunner.runLint(settings.cwd, settings.targetFile, SCSS_EXE, CONFIG); 34 | //// System.out.println(result.lint.file.name); 35 | //// System.out.println(result.lint.file.issues.size()); 36 | //// assertEquals("file name should match", scssFile, result.lint.get(scssFile)); 37 | // assertNotNull(result.lint); 38 | // assertEquals("should have 1 issue", 1, result.lint.get(scssFile).size()); 39 | // } 40 | 41 | @Test 42 | public void testFindAllExeFilesInPath() { 43 | List fromPath = PathEnvironmentVariableUtil.findAllExeFilesInPath(ScssLintFinder.SCSS_LINT_BASE_NAME); 44 | // System.out.println(fromPath); 45 | assertEquals("should have size", 2, fromPath.size()); 46 | assertArrayEquals("should have 2 paths", Arrays.asList("/usr/local/bin/scss-lint", "/usr/local/bin/scss-lint").toArray(), fromPath.stream().map(File::toString).toArray()); 47 | } 48 | 49 | // @Test 50 | // public void testVersion() { 51 | // ScssLintRunner.ScssLintSettings settings = createSettings(); 52 | // try { 53 | // String version = ScssLintRunner.runVersion(settings); 54 | // assertEquals("version should be", "scss-lint 0.27.0", version); 55 | // } catch (ExecutionException e) { 56 | // e.printStackTrace(); 57 | // } 58 | // } 59 | } 60 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/com/scsslint/utils/Settings.java: -------------------------------------------------------------------------------- 1 | package com.scsslint.utils; 2 | 3 | public final class Settings { 4 | static final String SCSS_EXE = "/usr/local/bin/scss-lint"; 5 | static final String PLUGIN_ROOT = "/Users/idok/projects/idea-plugins/scss-lint-plugin/scss-lint-plugin"; 6 | public static final String CONFIG = ""; 7 | 8 | private Settings() { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/java/log4j.properties: -------------------------------------------------------------------------------- 1 | 2 | # Set root logger level to DEBUG and its only appender to A1. 3 | log4j.rootLogger=WARN, A1 4 | 5 | # A1 is set to be a ConsoleAppender. 6 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 7 | 8 | # A1 uses PatternLayout. 9 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 11 | 12 | #com.intellij.ide.plugins.PluginBean -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/.scss-lint.yml: -------------------------------------------------------------------------------- 1 | #inherit_from: '../../inherited-config.yml' 2 | 3 | exclude: 'app/assets/stylesheets/plugins/**' 4 | 5 | linters: 6 | BorderZero: 7 | enabled: false 8 | 9 | Indentation: 10 | exclude: 11 | - 'path/to/file.scss' 12 | - 'path/to/directory/**' 13 | severity: warning 14 | width: 2 -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/inspections/CapitalizationInSelector.scss: -------------------------------------------------------------------------------- 1 | .shortOrLong { 2 | color: #f2e; 3 | } 4 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/inspections/EmptyRule.scss: -------------------------------------------------------------------------------- 1 | .cat { 2 | } -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/inspections/HexLength.scss: -------------------------------------------------------------------------------- 1 | 2 | .short-or-long { 3 | color: #ff22ee; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/inspections/one.scss: -------------------------------------------------------------------------------- 1 | $font-stack: Helvetica, sans-serif; 2 | $primary-color: #333; 3 | 4 | body { 5 | font: 100% $font-stack; 6 | color: $primary-color; 7 | } 8 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/one.scss: -------------------------------------------------------------------------------- 1 | $font-stack: Helvetica, sans-serif; 2 | $primary-color: #333; 3 | 4 | body { 5 | font: 100% $font-stack; 6 | color: $primary-color; 7 | } 8 | -------------------------------------------------------------------------------- /scss-lint-plugin/src/test/resources/one.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | --------------------------------------------------------------------------------