├── .gitignore
├── LICENSE
├── README.md
├── doc
├── config.png
├── inspect-inline.png
└── inspect.png
├── resources
├── META-INF
│ ├── plugin.xml
│ └── pluginIcon.svg
├── com
│ └── eslint
│ │ └── config
│ │ └── schema
│ │ └── schema.json
└── inspectionDescriptions
│ └── ESLintInspection.html
├── src
├── com
│ └── eslint
│ │ ├── ESLintBundle.java
│ │ ├── ESLintBundle.properties
│ │ ├── ESLintExternalAnnotator.java
│ │ ├── ESLintInspection.java
│ │ ├── ESLintProjectComponent.java
│ │ ├── actions
│ │ └── ESLintFixAction.java
│ │ ├── config
│ │ ├── ESLintConfigFileListener.java
│ │ ├── ESLintConfigFileType.java
│ │ ├── ESLintConfigFileTypeFactory.java
│ │ ├── ESLintConfigFileUtil.java
│ │ └── schema
│ │ │ ├── BaseType.java
│ │ │ ├── ESLintSchema.java
│ │ │ ├── RuleCache.java
│ │ │ ├── RuntimeTypeAdapterFactory.java
│ │ │ └── SchemaJsonObject.java
│ │ ├── fixes
│ │ ├── BaseActionFix.java
│ │ ├── DotNotationActionFix.java
│ │ ├── EqeqeqActionFix.java
│ │ ├── Fixes.java
│ │ ├── NoArrayConstructorActionFix.java
│ │ ├── NoLonelyIfActionFix.java
│ │ ├── NoNegatedInLhsActionFix.java
│ │ ├── NoNewBaseActionFix.java
│ │ ├── NoNewObjectActionFix.java
│ │ ├── StrictActionFix.java
│ │ ├── SuppressActionFix.java
│ │ └── SuppressLineActionFix.java
│ │ ├── inspection
│ │ └── PropertySuppressableInspectionBase.java
│ │ ├── settings
│ │ ├── ESLintSettingsPage.form
│ │ ├── ESLintSettingsPage.java
│ │ └── Settings.java
│ │ └── utils
│ │ ├── CliBuilder.java
│ │ ├── ESLintFinder.java
│ │ ├── ESLintRunner.java
│ │ ├── FileResult.java
│ │ ├── JSBinaryExpressionUtil.java
│ │ ├── Result.java
│ │ └── VerifyMessage.java
└── icons
│ ├── ESLintIcons.java
│ └── fileTypes
│ ├── eslint.png
│ └── eslint16x16.png
├── testData
├── .eslintrc
└── inspections
│ ├── eqeqeq.js
│ ├── no-array-constructor.js
│ ├── no-lonely-if.js
│ ├── no-negated-in-lhs.js
│ ├── no-new-object.js
│ └── valid-typeof.js
└── tests
└── com
└── eslint
├── ESLintRunnerTest.java
├── ESLintTest.java
└── TestUtils.java
/.gitignore:
--------------------------------------------------------------------------------
1 | ### OSX ###
2 | .DS_Store
3 |
4 | ### intellij ###
5 | .idea/
6 | out/
7 |
8 | *.iml
9 |
10 | ### plugin binary ###
11 | eslint-plugin.jar
12 | eslint-plugin.zip
13 |
14 | temp
15 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESLint Plugin #
2 |
3 | [ESLint](http://eslint.org/) is The pluggable linting utility for JavaScript. see more [here](http://eslint.org/).
4 | ESLint plugin for WebStorm, PHPStorm and other Idea family IDE with Javascript plugin, provides integration with ESLint and shows errors and warnings inside the editor.
5 | * Support displaying eslint warnings as intellij inspections
6 | * Quick fixes for several rules
7 | * Support for custom eslint rules
8 |
9 | ## Bundled plugin ##
10 | As of Intellij 14, a plugin based on this one was bundled into the IDE release by Jetbrains.
11 | What's the diffrence?
12 | This plugin supports Intellij 13 and other versions, --fix option, quick fixes and other minor differences.
13 | Please make sure you are referring to this one before opening an issue.
14 |
15 |
16 | ## Getting started ##
17 | ### Prerequisites ###
18 | * [NodeJS](http://nodejs.org/)
19 | * IntelliJ 13.1.4 / Webstorm 8.0.4, or above.
20 |
21 | Install eslint npm package [eslint npm](https://www.npmjs.org/package/eslint):
22 | ```bash
23 | $ cd
24 | $ npm install eslint
25 | ```
26 | Or, install eslint globally:
27 | ```bash
28 | $ npm install -g eslint
29 | ```
30 |
31 | ### Settings ###
32 | To get started, you need to set the ESLint plugin settings:
33 |
34 | * Go to preferences, ESLint plugin page and check the Enable plugin.
35 | * Set the path to the nodejs interpreter bin file.
36 | * Select whether to let eslint search for ```.eslintrc``` file
37 | * Set the path to the eslint bin file. should point to ```node_modules/eslint/bin/eslint.js``` if you installed locally or ```/usr/local/bin/eslint``` if you installed globally.
38 | * For Windows: install eslint globally and point to the eslint cmd file like, e.g. ```C:\Users\\AppData\Roaming\npm\eslint.cmd```
39 | * Set the ```.eslintrc``` file, or eslint will use the default settings.
40 | * You can also set a path to a custom rules directory.
41 | * By default, eslint plugin annotate the editor with warning or error based on the eslint configuration, you can check the 'Treat all eslint issues as warnings' checkbox to display all issues from eslint as warnings.
42 |
43 | Configuration:
44 | 
45 |
46 |
47 | Inspection:
48 | 
49 |
50 |
51 | Analyze Code:
52 | 
53 |
54 | ### A Note to contributors ###
55 | ESLint plugin uses the code from [here](https://github.com/idok/scss-lint-plugin/tree/master/intellij-common) as a module, to run the project you need to clone that project as well.
56 |
57 |
--------------------------------------------------------------------------------
/doc/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idok/eslint-plugin/08cead4d3a9da827e77513ea8300e6af881bef1b/doc/config.png
--------------------------------------------------------------------------------
/doc/inspect-inline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idok/eslint-plugin/08cead4d3a9da827e77513ea8300e6af881bef1b/doc/inspect-inline.png
--------------------------------------------------------------------------------
/doc/inspect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idok/eslint-plugin/08cead4d3a9da827e77513ea8300e6af881bef1b/doc/inspect.png
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.wix.eslint
3 | ESLint
4 | 1.0.36
5 | Ido
6 | HTML/JavaScript Development
7 |
9 | Support displaying eslint warnings as intellij inspections
10 | Quick fixes for several rules
11 | Support for custom eslint rules
12 | Support for eslint config annotation and completion
13 | ]]>
14 |
15 | 1.0.36 Bug fixes
17 | 1.0.35 Bug fixes
18 | 1.0.34 Bug fixes
19 | 1.0.33 Bug fixes
20 | 1.0.32 Bug fixes
21 | 1.0.31 Bug fixes
22 | 1.0.30 Fix NPE
23 | 1.0.29 Bug fixes
24 | 1.0.28 Fix default settings, support rc files with extensions, bug fixes
25 | 1.0.27 Bug fixes
26 | 1.0.26 Bug fixes
27 | 1.0.25 Add --ext. Bug fixes
28 | 1.0.24 Add --fix option as an action from code menu. Bug fixes
29 | 1.0.23 Class loading bug fixed
30 | 1.0.22 Intellij 14 / webstorm 9 compatible version
31 | 1.0.21 Bug fixes, doesn't take tab size into account
32 | 1.0.20 Add settings for
33 | eslint builtin rules directory to support completion and annotation on eslintrc files
34 | 1.0.19 Fix windows execution and solve version compatibility issue, thanks eric-isakson
35 | 1.0.18 fix plugin url
36 | 1.0.17 fix default eslint bin
37 | 1.0.16 fix issue finding eslint bin in windows
38 | 1.0.15 fix lag in settings dialog, fix windows issue with running lint
39 | 1.0.14 fix eslintrc configuration in settings
40 | 1.0.13 bug fix
41 | 1.0.12 bug fix related to relative path of eslint
42 | 1.0.11 bug fixes, add quick fix to DotNotation rule
43 | 1.0.10 Add annotation and completion to eslintrc, add version to settings dialog, fix eslintrc not loading bug
44 | 1.0.9 Fix performance issue
45 | 1.0.8 Performance improvements, bug fixes, refresh inspection status when eslintrc or config change
46 | 1.0.7 Fix ClassNotFound Error
47 | 1.0.6 Add configuration options for node interpreter and eslintrc search, bug fixes
48 | 1.0.1 First version.
49 | ]]>
50 |
51 | com.intellij.modules.lang
52 |
53 | JavaScript
54 |
55 |
56 |
57 |
58 |
59 |
61 |
64 |
65 |
66 |
67 |
68 |
69 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | com.eslint.ESLintProjectComponent
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
10 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/com/eslint/config/schema/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "root",
3 | "type": "OBJECT",
4 | "description": "",
5 | "properties": [
6 | {
7 | "title": "env",
8 | "type": "OBJECT",
9 | "description": "running environment",
10 | "properties": [
11 | {
12 | "title": "amd",
13 | "type": "BOOLEAN",
14 | "description": "amd"
15 | },
16 | {
17 | "title": "node",
18 | "type": "BOOLEAN",
19 | "description": "node"
20 | },
21 | {
22 | "title": "browser",
23 | "type": "BOOLEAN",
24 | "description": "browser"
25 | }
26 | ]
27 | },
28 | {
29 | "title": "globals",
30 | "type": "OBJECT",
31 | "description": "globals",
32 | "properties": [
33 | {
34 | "title": "*",
35 | "type": "BOOLEAN",
36 | "description": ""
37 | }
38 | ]
39 | },
40 | {
41 | "title": "rules",
42 | "type": "OBJECT",
43 | "description": "rules",
44 | "properties": []
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/resources/inspectionDescriptions/ESLintInspection.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Runs ESLint validator for specified JavaScript file.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/com/eslint/ESLintBundle.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
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 ESLintBundle {
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.eslint.ESLintBundle";
20 | private static Reference ourBundle;
21 |
22 | @NonNls
23 | public static final String LOG_ID = "#com.eslint";
24 |
25 | private ESLintBundle() {
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 | }
--------------------------------------------------------------------------------
/src/com/eslint/ESLintBundle.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 | eslint.inspection.group.name=Code quality tools
8 | eslint.inspection.undefined.step.name=Undefined step
9 | eslint.inspection.undefined.step.msg.name=Undefined step reference:
10 | eslint.property.inspection.display.name=ESLint
11 | eslint.property.inspection.message=ESLint: {0} ({1})
12 |
13 | eslint.rules.dir.does.not.exist=Rules directory not found. Path {0} not found in project
14 | eslint.rules.dir.is.not.a.dir=Rules path is not a directory. Path {0} not found in project
15 |
16 | eslint.directory.does.not.exist={0} directory not found. Path {1} not found in project
17 | eslint.directory.is.not.a.dir={0} path is not a directory. Path {1} not found in project
18 | eslint.file.does.not.exist={0} file not found. Path {1} not found in project
19 | eslint.file.is.not.a.file={0} path is not a file. Path {1} not found in project
20 |
21 | inspection.fix.strict=Add 'use strict' statement
22 | inspection.fix.no.new.object=Convert to {}
23 | inspection.fix.no-array-constructor=Convert to []
24 | inspection.fix.eqeqeq=Convert to ===
25 | inspection.fix.no-lonely-if=Convert to else if
26 | inspection.fix.no-negated-in-lhs=Wrap with parenthesis
27 | inspection.fix.dot-notation=Convert to dot notation
28 |
29 |
30 | properties.files.inspection.group.display.name=ESLint
31 |
--------------------------------------------------------------------------------
/src/com/eslint/ESLintExternalAnnotator.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
2 |
3 | import com.eslint.config.ESLintConfigFileListener;
4 | import com.eslint.fixes.BaseActionFix;
5 | import com.eslint.fixes.Fixes;
6 | import com.eslint.fixes.SuppressActionFix;
7 | import com.eslint.fixes.SuppressLineActionFix;
8 | import com.eslint.settings.ESLintSettingsPage;
9 | import com.eslint.utils.ESLintRunner;
10 | import com.eslint.utils.Result;
11 | import com.eslint.utils.VerifyMessage;
12 | import com.intellij.codeInsight.daemon.HighlightDisplayKey;
13 | import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
14 | import com.intellij.lang.annotation.Annotation;
15 | import com.intellij.lang.annotation.AnnotationHolder;
16 | import com.intellij.lang.annotation.ExternalAnnotator;
17 | import com.intellij.lang.annotation.HighlightSeverity;
18 | import com.intellij.lang.javascript.JavaScriptFileType;
19 | import com.intellij.lang.javascript.linter.JSLinterUtil;
20 | import com.intellij.lang.javascript.psi.JSFile;
21 | import com.intellij.notification.NotificationType;
22 | import com.intellij.openapi.application.ApplicationManager;
23 | import com.intellij.openapi.application.ModalityState;
24 | import com.intellij.openapi.application.WriteAction;
25 | import com.intellij.openapi.diagnostic.Logger;
26 | import com.intellij.openapi.editor.Document;
27 | import com.intellij.openapi.editor.Editor;
28 | import com.intellij.openapi.editor.colors.EditorColorsScheme;
29 | import com.intellij.openapi.editor.markup.TextAttributes;
30 | import com.intellij.openapi.project.Project;
31 | import com.intellij.openapi.util.Key;
32 | import com.intellij.openapi.util.TextRange;
33 | import com.intellij.openapi.util.text.StringUtil;
34 | import com.intellij.openapi.vfs.VirtualFile;
35 | import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
36 | import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider;
37 | import com.intellij.psi.PsiDocumentManager;
38 | import com.intellij.psi.PsiElement;
39 | import com.intellij.psi.PsiFile;
40 | import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
41 | import com.intellij.util.ThrowableRunnable;
42 | import com.intellij.util.ui.UIUtil;
43 | import com.wix.ActualFile;
44 | import com.wix.ThreadLocalActualFile;
45 | import com.wix.annotator.ExternalLintAnnotationInput;
46 | import com.wix.annotator.ExternalLintAnnotationResult;
47 | import com.wix.utils.FileUtils;
48 | import com.wix.utils.PsiUtil;
49 | import org.apache.commons.lang.StringUtils;
50 | import org.jetbrains.annotations.NotNull;
51 | import org.jetbrains.annotations.Nullable;
52 |
53 | import java.io.File;
54 |
55 | /**
56 | * @author idok
57 | */
58 | public class ESLintExternalAnnotator extends ExternalAnnotator> {
59 |
60 | // public static final ESLintExternalAnnotator INSTANCE = new ESLintExternalAnnotator();
61 | private static final Logger LOG = Logger.getInstance(ESLintBundle.LOG_ID);
62 | private static final String MESSAGE_PREFIX = "ESLint: ";
63 | private static final Key ESLINT_TEMP_FILE_KEY = Key.create("ESLINT_TEMP_FILE");
64 | // private static final int TABS = 4;
65 | // private int tabSize;
66 |
67 | // private static int getTabSize(@NotNull Editor editor) {
68 | // // Get tab size
69 | // int tabSize = 0;
70 | // Project project = editor.getProject();
71 | // PsiFile psifile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
72 | // CommonCodeStyleSettings commonCodeStyleSettings = new CommonCodeStyleSettings(psifile.getLanguage());
73 | // CommonCodeStyleSettings.IndentOptions indentOptions = commonCodeStyleSettings.getIndentOptions();
74 | //
75 | // if (indentOptions != null) {
76 | // tabSize = commonCodeStyleSettings.getIndentOptions().TAB_SIZE;
77 | // }
78 | // if (tabSize == 0) {
79 | // tabSize = editor.getSettings().getTabSize(editor.getProject());
80 | // }
81 | // return tabSize;
82 | // }
83 |
84 | @Nullable
85 | @Override
86 | public ExternalLintAnnotationInput collectInformation(@NotNull PsiFile file) {
87 | return collectInformation(file, null);
88 | }
89 |
90 | @Nullable
91 | @Override
92 | public ExternalLintAnnotationInput collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) {
93 | return collectInformation(file, editor);
94 | }
95 |
96 | @NotNull
97 | public static HighlightDisplayKey getHighlightDisplayKeyByClass() {
98 | String id = "ESLint";
99 | HighlightDisplayKey key = HighlightDisplayKey.find(id);
100 | if (key == null) {
101 | key = new HighlightDisplayKey(id, id);
102 | }
103 | return key;
104 | }
105 |
106 | @Override
107 | public void apply(@NotNull PsiFile file, ExternalLintAnnotationResult annotationResult, @NotNull AnnotationHolder holder) {
108 | if (annotationResult == null) {
109 | return;
110 | }
111 | InspectionProjectProfileManager inspectionProjectProfileManager = InspectionProjectProfileManager.getInstance(file.getProject());
112 | SeverityRegistrar severityRegistrar = inspectionProjectProfileManager.getSeverityRegistrar();
113 | HighlightDisplayKey inspectionKey = getHighlightDisplayKeyByClass();
114 | EditorColorsScheme colorsScheme = annotationResult.input.colorsScheme;
115 |
116 | Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
117 | if (document == null) {
118 | return;
119 | }
120 | ESLintProjectComponent component = annotationResult.input.project.getComponent(ESLintProjectComponent.class);
121 | for (VerifyMessage warn : annotationResult.result.warns) {
122 | HighlightSeverity severity = getHighlightSeverity(warn, component.treatAsWarnings);
123 | TextAttributes forcedTextAttributes = JSLinterUtil.getTextAttributes(colorsScheme, severityRegistrar, severity);
124 | Annotation annotation = createAnnotation(holder, file, document, warn, severity, forcedTextAttributes, false);
125 | if (annotation != null) {
126 | int offset = StringUtil.lineColToOffset(document.getText(), warn.line - 1, warn.column);
127 | PsiElement lit = PsiUtil.getElementAtOffset(file, offset);
128 | BaseActionFix actionFix = Fixes.getFixForRule(warn.ruleId, lit);
129 | if (actionFix != null) {
130 | annotation.registerFix(actionFix, null, inspectionKey);
131 | }
132 | annotation.registerFix(new SuppressActionFix(warn.ruleId, lit), null, inspectionKey);
133 | annotation.registerFix(new SuppressLineActionFix(warn.ruleId, lit), null, inspectionKey);
134 | }
135 | }
136 | }
137 |
138 | private static HighlightSeverity getHighlightSeverity(VerifyMessage warn, boolean treatAsWarnings) {
139 | if (treatAsWarnings) {
140 | return HighlightSeverity.WARNING;
141 | }
142 | return warn.severity == 2 ? HighlightSeverity.ERROR : HighlightSeverity.WARNING;
143 | }
144 |
145 | @Nullable
146 | private static Annotation createAnnotation(@NotNull AnnotationHolder holder, @NotNull PsiFile file, @NotNull Document document, @NotNull VerifyMessage warn,
147 | @NotNull HighlightSeverity severity, @Nullable TextAttributes forcedTextAttributes,
148 | boolean showErrorOnWholeLine) {
149 | int line = warn.line - 1;
150 | int column = warn.column - 1;
151 |
152 | if (line < 0 || line >= document.getLineCount()) {
153 | return null;
154 | }
155 | int lineEndOffset = document.getLineEndOffset(line);
156 | int lineStartOffset = document.getLineStartOffset(line);
157 |
158 | int errorLineStartOffset = StringUtil.lineColToOffset(document.getCharsSequence(), line, column);
159 | // int errorLineStartOffset = PsiUtil.calcErrorStartOffsetInDocument(document, lineStartOffset, lineEndOffset, column, tab);
160 |
161 | if (errorLineStartOffset == -1) {
162 | return null;
163 | }
164 | // PsiElement element = file.findElementAt(errorLineStartOffset);
165 | TextRange range;
166 | if (showErrorOnWholeLine) {
167 | range = new TextRange(lineStartOffset, lineEndOffset);
168 | } else {
169 | // int offset = StringUtil.lineColToOffset(document.getText(), warn.line - 1, warn.column);
170 | PsiElement lit = PsiUtil.getElementAtOffset(file, errorLineStartOffset);
171 | range = lit.getTextRange();
172 | // range = new TextRange(errorLineStartOffset, errorLineStartOffset + 1);
173 | }
174 |
175 | Annotation annotation = JSLinterUtil.createAnnotation(holder, severity, forcedTextAttributes, range, MESSAGE_PREFIX + warn.message.trim() + " (" + warn.ruleId + ')');
176 | if (annotation != null) {
177 | annotation.setAfterEndOfLine(errorLineStartOffset == lineEndOffset);
178 | }
179 | return annotation;
180 | }
181 |
182 | @Nullable
183 | private static ExternalLintAnnotationInput collectInformation(@NotNull PsiFile psiFile, @Nullable Editor editor) {
184 | if (psiFile.getContext() != null) {
185 | return null;
186 | }
187 | VirtualFile virtualFile = psiFile.getVirtualFile();
188 | if (virtualFile == null || !virtualFile.isInLocalFileSystem()) {
189 | return null;
190 | }
191 | if (psiFile.getViewProvider() instanceof MultiplePsiFilesPerDocumentFileViewProvider) {
192 | return null;
193 | }
194 | Project project = psiFile.getProject();
195 | ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
196 | if (!component.isSettingsValid() || !component.isEnabled() || !isJavaScriptFile(psiFile, component.ext)) {
197 | return null;
198 | }
199 | Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
200 | if (document == null) {
201 | return null;
202 | }
203 | String fileContent = document.getText();
204 | if (StringUtil.isEmptyOrSpaces(fileContent)) {
205 | return null;
206 | }
207 | EditorColorsScheme colorsScheme = editor == null ? null : editor.getColorsScheme();
208 | // tabSize = getTabSize(editor);
209 | // tabSize = 4;
210 | return new ExternalLintAnnotationInput(project, psiFile, fileContent, colorsScheme);
211 | }
212 |
213 | private static boolean isInList(String file, String ext) {
214 | if (StringUtils.isEmpty(ext)) {
215 | return false;
216 | }
217 | String[] exts = ext.split(",");
218 | for (String ex : exts) {
219 | if (file.endsWith(ex)) {
220 | return true;
221 | }
222 | }
223 | return false;
224 | }
225 |
226 | private static boolean isJavaScriptFile(PsiFile file, String ext) {
227 | return file instanceof JSFile && file.getFileType().equals(JavaScriptFileType.INSTANCE) || (!StringUtils.isEmpty(ext) && isInList(file.getName(), ext));
228 | }
229 |
230 | @Nullable
231 | @Override
232 | public ExternalLintAnnotationResult doAnnotate(ExternalLintAnnotationInput collectedInfo) {
233 | try {
234 | LOG.info("Running ESLint inspection");
235 | PsiFile file = collectedInfo.psiFile;
236 | Project project = file.getProject();
237 | ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
238 | if (!component.isSettingsValid() || !component.isEnabled() || !isJavaScriptFile(file, component.ext)) {
239 | return null;
240 | }
241 | ESLintConfigFileListener.start(collectedInfo.project);
242 | String relativeFile;
243 | ActualFile actualCodeFile = ActualFile.getOrCreateActualFile(ESLINT_TEMP_FILE_KEY, file.getVirtualFile(), collectedInfo.fileContent);
244 | if (actualCodeFile == null || actualCodeFile.getFile() == null) {
245 | return null;
246 | }
247 | relativeFile = FileUtils.makeRelative(new File(project.getBasePath()), actualCodeFile.getActualFile());
248 | Result result = ESLintRunner.lint(project.getBasePath(), relativeFile, component);
249 |
250 | if (component.settings.autoFix) {
251 | // Document document = PsiDocumentManager.getInstance(project).getDocument(file);
252 | // document.
253 | // Document document = PsiDocumentManager.getInstance(project).getDocument(file);
254 | // read lock
255 | // ApplicationManager.getApplication().runWriteAction()
256 | // ApplicationManager.getApplication().runWriteAction()
257 | ApplicationManager.getApplication().invokeLater(new Runnable() {
258 | public void run() {
259 | file.getVirtualFile().refresh(false, false);
260 | }
261 | }, ModalityState.NON_MODAL);
262 |
263 | // WriteAction.run(() -> file.getVirtualFile().refresh(false, false));
264 | // UIUtil.invokeLaterIfNeeded(new Runnable() {
265 | // public void run() {
266 | // file.getVirtualFile().refresh(false, false);
267 | // }
268 | // });
269 | }
270 |
271 | actualCodeFile.deleteTemp();
272 | if (StringUtils.isNotEmpty(result.errorOutput)) {
273 | component.showInfoNotification(result.errorOutput, NotificationType.WARNING);
274 | return null;
275 | }
276 | Document document = PsiDocumentManager.getInstance(project).getDocument(file);
277 | if (document == null) {
278 | component.showInfoNotification("Error running ESLint inspection: Could not get document for file " + file.getName(), NotificationType.WARNING);
279 | LOG.error("Could not get document for file " + file.getName());
280 | return null;
281 | }
282 | return new ExternalLintAnnotationResult<>(collectedInfo, result);
283 | } catch (Exception e) {
284 | LOG.error("Error running ESLint inspection: ", e);
285 | ESLintProjectComponent.showNotification("Error running ESLint inspection: " + e.getMessage(), NotificationType.ERROR);
286 | }
287 | return null;
288 | }
289 | }
--------------------------------------------------------------------------------
/src/com/eslint/ESLintInspection.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
2 |
3 | import com.eslint.inspection.PropertySuppressableInspectionBase;
4 | import com.eslint.settings.ESLintSettingsPage;
5 | import com.google.common.base.Joiner;
6 | import com.intellij.codeInspection.*;
7 | import com.intellij.codeInspection.ex.UnfairLocalInspectionTool;
8 | import com.intellij.ide.DataManager;
9 | import com.intellij.ide.actions.ShowSettingsUtilImpl;
10 | import com.intellij.lang.javascript.JSBundle;
11 | import com.intellij.openapi.actionSystem.CommonDataKeys;
12 | import com.intellij.openapi.actionSystem.DataContext;
13 | import com.intellij.openapi.diagnostic.Logger;
14 | import com.intellij.openapi.options.Configurable;
15 | import com.intellij.openapi.options.ex.SingleConfigurableEditor;
16 | //import com.intellij.openapi.options.newEditor.OptionsEditor;
17 | import com.intellij.openapi.project.Project;
18 | import com.intellij.openapi.util.Key;
19 | import com.intellij.psi.PsiElementVisitor;
20 | import com.intellij.psi.PsiFile;
21 | import com.intellij.ui.HyperlinkAdapter;
22 | import com.intellij.ui.HyperlinkLabel;
23 | import com.intellij.ui.IdeBorderFactory;
24 | import com.intellij.util.containers.ContainerUtil;
25 | import org.jetbrains.annotations.NotNull;
26 |
27 | import javax.swing.*;
28 | import javax.swing.event.HyperlinkEvent;
29 | import java.awt.*;
30 | import java.util.List;
31 |
32 | public class ESLintInspection extends PropertySuppressableInspectionBase implements UnfairLocalInspectionTool { //extends PropertySuppressableInspectionBase {
33 |
34 | public static final String INSPECTION_SHORT_NAME = "ESLintInspection";
35 | public static final Key KEY = Key.create(INSPECTION_SHORT_NAME);
36 |
37 | private static final Logger LOG = Logger.getInstance(ESLintBundle.LOG_ID);
38 |
39 | @NotNull
40 | public String getDisplayName() {
41 | return ESLintBundle.message("eslint.property.inspection.display.name");
42 | }
43 |
44 | @NotNull
45 | public String getShortName() {
46 | return INSPECTION_SHORT_NAME;
47 | }
48 |
49 | public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
50 | return ExternalAnnotatorInspectionVisitor.checkFileWithExternalAnnotator(file, manager, isOnTheFly, new ESLintExternalAnnotator());
51 | }
52 |
53 | @NotNull
54 | @Override
55 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
56 | return new ExternalAnnotatorInspectionVisitor(holder, new ESLintExternalAnnotator(), isOnTheFly);
57 | }
58 |
59 | public JComponent createOptionsPanel() {
60 | JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
61 | HyperlinkLabel settingsLink = createHyperLink();
62 | panel.setBorder(IdeBorderFactory.createTitledBorder(getDisplayName() + " options"));
63 | panel.add(settingsLink);
64 | return panel;
65 | }
66 |
67 | @NotNull
68 | public String getId() {
69 | return "Settings.JavaScript.Linters.ESLint";
70 | }
71 |
72 | @NotNull
73 | private HyperlinkLabel createHyperLink() {
74 | List path = ContainerUtil.newArrayList(JSBundle.message("settings.javascript.root.configurable.name"), JSBundle.message("settings.javascript.linters.configurable.name"), getDisplayName());
75 |
76 | String title = Joiner.on(" / ").join(path);
77 | final HyperlinkLabel settingsLink = new HyperlinkLabel(title);
78 | settingsLink.addHyperlinkListener(new HyperlinkAdapter() {
79 | public void hyperlinkActivated(HyperlinkEvent e) {
80 | // DataContext dataContext = DataManager.getInstance().getDataContext(settingsLink);
81 | // OptionsEditor optionsEditor = OptionsEditor.KEY.getData(dataContext);
82 | // if (optionsEditor == null) {
83 | // Project project = CommonDataKeys.PROJECT.getData(dataContext);
84 | // if (project != null) {
85 | // showSettings(project);
86 | // }
87 | // return;
88 | // }
89 | // Configurable configurable = optionsEditor.findConfigurableById(ESLintInspection.this.getId());
90 | // if (configurable != null) {
91 | // optionsEditor.clearSearchAndSelect(configurable);
92 | // }
93 | }
94 | });
95 | return settingsLink;
96 | }
97 |
98 | public static void showSettings(Project project) {
99 | ESLintSettingsPage configurable = new ESLintSettingsPage(project);
100 | String dimensionKey = ShowSettingsUtilImpl.createDimensionKey(configurable);
101 | SingleConfigurableEditor singleConfigurableEditor = new SingleConfigurableEditor(project, configurable, dimensionKey, false);
102 | singleConfigurableEditor.show();
103 | }
104 |
105 | // @Override
106 | // public boolean isSuppressedFor(@NotNull PsiElement element) {
107 | // return false;
108 | // }
109 |
110 | // @NotNull
111 | // @Override
112 | // public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) {
113 | // return super.getBatchSuppressActions(element);
114 | // return new SuppressQuickFix[0];
115 | // }
116 | }
--------------------------------------------------------------------------------
/src/com/eslint/ESLintProjectComponent.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
2 |
3 | import com.eslint.config.schema.RuleCache;
4 | import com.eslint.settings.Settings;
5 | import com.intellij.notification.Notification;
6 | import com.intellij.notification.NotificationListener;
7 | import com.intellij.notification.NotificationType;
8 | import com.intellij.notification.Notifications;
9 | import com.intellij.openapi.components.ProjectComponent;
10 | import com.intellij.openapi.diagnostic.Logger;
11 | import com.intellij.openapi.project.Project;
12 | import com.wix.utils.FileUtils;
13 | import com.wix.utils.FileUtils.ValidationStatus;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import javax.swing.event.HyperlinkEvent;
17 |
18 | public class ESLintProjectComponent implements ProjectComponent {
19 | public static final String FIX_CONFIG_HREF = "\nFix Configuration ";
20 | protected Project project;
21 | protected Settings settings;
22 | protected boolean settingValidStatus;
23 | protected String settingValidVersion;
24 | protected String settingVersionLastShowNotification;
25 |
26 | private static final Logger LOG = Logger.getInstance(ESLintBundle.LOG_ID);
27 |
28 | public String eslintRcFile;
29 | public String customRulesPath;
30 | public String ext;
31 | public String rulesPath;
32 | public String eslintExecutable;
33 | public String nodeInterpreter;
34 | public boolean treatAsWarnings;
35 | public boolean pluginEnabled;
36 | public boolean autoFix;
37 | public boolean reportUnused;
38 |
39 | public static final String PLUGIN_NAME = "ESLint plugin";
40 |
41 | public ESLintProjectComponent(Project project) {
42 | this.project = project;
43 | settings = Settings.getInstance(project);
44 | }
45 |
46 | @Override
47 | public void projectOpened() {
48 | if (isEnabled()) {
49 | isSettingsValid();
50 | }
51 | }
52 |
53 | @Override
54 | public void projectClosed() {
55 | }
56 |
57 | @Override
58 | public void initComponent() {
59 | if (isEnabled()) {
60 | isSettingsValid();
61 | }
62 | }
63 |
64 | @Override
65 | public void disposeComponent() {
66 | }
67 |
68 | @NotNull
69 | @Override
70 | public String getComponentName() {
71 | return "ESLintProjectComponent";
72 | }
73 |
74 | public boolean isEnabled() {
75 | return Settings.getInstance(project).pluginEnabled;
76 | }
77 |
78 | public boolean isSettingsValid() {
79 | if (!settings.getVersion().equals(settingValidVersion)) {
80 | validateSettings();
81 | settingValidVersion = settings.getVersion();
82 | }
83 | return settingValidStatus;
84 | }
85 |
86 | public boolean validateSettings() {
87 | // do not validate if disabled
88 | if (!settings.pluginEnabled) {
89 | return true;
90 | }
91 | boolean status = validateField("Node Interpreter", settings.nodeInterpreter, true, false, true);
92 | if (!status) {
93 | return false;
94 | }
95 | status = validateField("Rules", settings.rulesPath, false, true, false);
96 | if (!status) {
97 | return false;
98 | }
99 | status = validateField("ESLint bin", settings.eslintExecutable, false, false, true);
100 | if (!status) {
101 | return false;
102 | }
103 | status = validateField("Builtin rules", settings.builtinRulesPath, false, true, false);
104 | if (!status) {
105 | return false;
106 | }
107 |
108 | // if (StringUtil.isNotEmpty(settings.eslintExecutable)) {
109 | // File file = new File(project.getBasePath(), settings.eslintExecutable);
110 | // if (!file.exists()) {
111 | // showErrorConfigNotification(ESLintBundle.message("eslint.rules.dir.does.not.exist", file.toString()));
112 | // LOG.debug("Rules directory not found");
113 | // settingValidStatus = false;
114 | // return false;
115 | // }
116 | // }
117 | eslintExecutable = settings.eslintExecutable;
118 | eslintRcFile = settings.eslintRcFile;
119 | customRulesPath = settings.rulesPath;
120 | rulesPath = settings.builtinRulesPath;
121 | nodeInterpreter = settings.nodeInterpreter;
122 | treatAsWarnings = settings.treatAllEslintIssuesAsWarnings;
123 | pluginEnabled = settings.pluginEnabled;
124 | ext = settings.ext;
125 | autoFix = settings.autoFix;
126 | reportUnused = settings.reportUnused;
127 |
128 | RuleCache.initializeFromPath(project, this);
129 |
130 | settingValidStatus = true;
131 | return true;
132 | }
133 |
134 | private boolean validateField(String fieldName, String value, boolean shouldBeAbsolute, boolean allowEmpty, boolean isFile) {
135 | ValidationStatus r = FileUtils.validateProjectPath(shouldBeAbsolute ? null : project, value, allowEmpty, isFile);
136 | if (isFile) {
137 | if (r == ValidationStatus.NOT_A_FILE) {
138 | String msg = ESLintBundle.message("eslint.file.is.not.a.file", fieldName, value);
139 | validationFailed(msg);
140 | return false;
141 | }
142 | } else {
143 | if (r == ValidationStatus.NOT_A_DIRECTORY) {
144 | String msg = ESLintBundle.message("eslint.directory.is.not.a.dir", fieldName, value);
145 | validationFailed(msg);
146 | return false;
147 | }
148 | }
149 | if (r == ValidationStatus.DOES_NOT_EXIST) {
150 | String msg = ESLintBundle.message("eslint.file.does.not.exist", fieldName, value);
151 | validationFailed(msg);
152 | return false;
153 | }
154 | return true;
155 | }
156 |
157 | private void validationFailed(String msg) {
158 | NotificationListener notificationListener = new NotificationListener() {
159 | @Override
160 | public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
161 | ESLintInspection.showSettings(project);
162 | }
163 | };
164 | String errorMessage = msg + FIX_CONFIG_HREF;
165 | showInfoNotification(errorMessage, NotificationType.WARNING, notificationListener);
166 | LOG.debug(msg);
167 | settingValidStatus = false;
168 | }
169 |
170 | protected void showErrorConfigNotification(String content) {
171 | if (!settings.getVersion().equals(settingVersionLastShowNotification)) {
172 | settingVersionLastShowNotification = settings.getVersion();
173 | showInfoNotification(content, NotificationType.WARNING);
174 | }
175 | }
176 |
177 | public void showInfoNotification(String content, NotificationType type) {
178 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type);
179 | Notifications.Bus.notify(errorNotification, this.project);
180 | }
181 |
182 | public void showInfoNotification(String content, NotificationType type, NotificationListener notificationListener) {
183 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type, notificationListener);
184 | Notifications.Bus.notify(errorNotification, this.project);
185 | }
186 |
187 | public static void showNotification(String content, NotificationType type) {
188 | Notification errorNotification = new Notification(PLUGIN_NAME, PLUGIN_NAME, content, type);
189 | Notifications.Bus.notify(errorNotification);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/com/eslint/actions/ESLintFixAction.java:
--------------------------------------------------------------------------------
1 | package com.eslint.actions;
2 |
3 | import com.eslint.ESLintProjectComponent;
4 | import com.eslint.utils.ESLintRunner;
5 | import com.intellij.execution.ExecutionException;
6 | import com.intellij.openapi.actionSystem.AnAction;
7 | import com.intellij.openapi.actionSystem.AnActionEvent;
8 | import com.intellij.openapi.actionSystem.DataConstants;
9 | import com.intellij.openapi.project.DumbAware;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.openapi.vfs.VirtualFile;
12 |
13 | public class ESLintFixAction extends AnAction implements DumbAware {
14 |
15 | public void actionPerformed(AnActionEvent e) {
16 | final Project project = e.getProject();
17 | if (project == null) return;
18 | final VirtualFile file = (VirtualFile) e.getDataContext().getData(DataConstants.VIRTUAL_FILE);
19 |
20 | // TODO handle multiple selection
21 | if (file == null) {
22 | // File[] rtFiles = RTFile.DATA_KEY.getData(e.getDataContext());
23 | // if (rtFiles == null || rtFiles.length == 0) {
24 | // System.out.println("No file for rt compile");
25 | // return;
26 | // }
27 | // // handle all files
28 | // for (RTFile rtFile : rtFiles) {
29 | // RTFileListener.compile(rtFile.getRtFile().getVirtualFile(), project);
30 | // }
31 | } else {
32 | ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
33 | if (!component.isSettingsValid() || !component.isEnabled()) {
34 | return;
35 | }
36 | // Result result = ESLintRunner.lint(project.getBasePath(), relativeFile, component.nodeInterpreter, component.eslintExecutable, component.eslintRcFile, component.customRulesPath);
37 |
38 | if (project.getBasePath() != null) {
39 | ESLintRunner.ESLintSettings settings = ESLintRunner.buildSettings(project.getBasePath(), file.getPath(), component);
40 | try {
41 | ESLintRunner.fix(settings);
42 | file.refresh(false, false);
43 | } catch (ExecutionException e1) {
44 | e1.printStackTrace();
45 | }
46 | }
47 | }
48 | }
49 |
50 | //TODO implement update, disable when not relevant?
51 | // add project view popup
52 | //fix menu location
53 | }
54 |
--------------------------------------------------------------------------------
/src/com/eslint/config/ESLintConfigFileListener.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config;
2 |
3 | import com.eslint.ESLintProjectComponent;
4 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
5 | import com.intellij.openapi.application.ApplicationManager;
6 | import com.intellij.openapi.components.ServiceManager;
7 | import com.intellij.openapi.editor.EditorFactory;
8 | import com.intellij.openapi.editor.event.DocumentAdapter;
9 | import com.intellij.openapi.editor.event.DocumentEvent;
10 | import com.intellij.openapi.editor.event.EditorEventMulticaster;
11 | import com.intellij.openapi.fileEditor.FileDocumentManager;
12 | import com.intellij.openapi.project.Project;
13 | import com.intellij.openapi.vfs.*;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.concurrent.atomic.AtomicBoolean;
17 |
18 | public class ESLintConfigFileListener {
19 | private final Project project;
20 | private final AtomicBoolean LISTENING = new AtomicBoolean(false);
21 |
22 | public ESLintConfigFileListener(@NotNull Project project) {
23 | this.project = project;
24 | }
25 |
26 | private void startListener() {
27 | if (LISTENING.compareAndSet(false, true))
28 | ApplicationManager.getApplication().invokeLater(new Runnable() {
29 | public void run() {
30 | ApplicationManager.getApplication().runWriteAction(new Runnable() {
31 | public void run() {
32 | VirtualFileManager.getInstance().addVirtualFileListener(new ESLintConfigFileVfsListener(), ESLintConfigFileListener.this.project);
33 | EditorEventMulticaster multicaster = EditorFactory.getInstance().getEventMulticaster();
34 | multicaster.addDocumentListener(new ESLintConfigFileDocumentListener(), ESLintConfigFileListener.this.project);
35 | }
36 | });
37 | }
38 | });
39 | }
40 |
41 | public static void start(@NotNull Project project) {
42 | ESLintConfigFileListener listener = ServiceManager.getService(project, ESLintConfigFileListener.class);
43 | listener.startListener();
44 | }
45 |
46 | private void fileChanged(@NotNull VirtualFile file) {
47 | if (ESLintConfigFileUtil.isESLintConfigFile(file) && !project.isDisposed()) {
48 | restartAnalyzer();
49 | }
50 | }
51 |
52 | private void restartAnalyzer() {
53 | ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
54 | if (component.isEnabled()) {
55 | DaemonCodeAnalyzer.getInstance(project).restart();
56 | }
57 | }
58 |
59 | /**
60 | * VFS Listener
61 | */
62 | private class ESLintConfigFileVfsListener extends VirtualFileAdapter {
63 | private ESLintConfigFileVfsListener() {
64 | }
65 |
66 | public void fileCreated(@NotNull VirtualFileEvent event) {
67 | ESLintConfigFileListener.this.fileChanged(event.getFile());
68 | }
69 |
70 | public void fileDeleted(@NotNull VirtualFileEvent event) {
71 | ESLintConfigFileListener.this.fileChanged(event.getFile());
72 | }
73 |
74 | public void fileMoved(@NotNull VirtualFileMoveEvent event) {
75 | ESLintConfigFileListener.this.fileChanged(event.getFile());
76 | }
77 |
78 | public void fileCopied(@NotNull VirtualFileCopyEvent event) {
79 | ESLintConfigFileListener.this.fileChanged(event.getFile());
80 | ESLintConfigFileListener.this.fileChanged(event.getOriginalFile());
81 | }
82 | }
83 |
84 | /**
85 | * Document Listener
86 | */
87 | private class ESLintConfigFileDocumentListener extends DocumentAdapter {
88 | private ESLintConfigFileDocumentListener() {
89 | }
90 |
91 | public void documentChanged(DocumentEvent event) {
92 | VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument());
93 | if (file != null) {
94 | ESLintConfigFileListener.this.fileChanged(file);
95 | }
96 | }
97 | }
98 | }
99 |
100 |
--------------------------------------------------------------------------------
/src/com/eslint/config/ESLintConfigFileType.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config;
2 |
3 | import com.intellij.json.JsonLanguage;
4 | //import com.intellij.lang.javascript.json.JSONLanguageDialect;
5 | import com.intellij.openapi.fileTypes.LanguageFileType;
6 |
7 | import javax.swing.Icon;
8 |
9 | import icons.ESLintIcons;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | public class ESLintConfigFileType extends LanguageFileType {
13 | public static final ESLintConfigFileType INSTANCE = new ESLintConfigFileType();
14 | public static final String ESLINTRC_EXT = "eslintrc";
15 | public static final String ESLINTRC = '.' + ESLINTRC_EXT;
16 | public static final String[] ESLINTRC_FILES = {ESLINTRC, ESLINTRC + ".js", ESLINTRC + ".yml", ESLINTRC + ".yaml", ESLINTRC + ".json"};
17 |
18 | private ESLintConfigFileType() {
19 | super(JsonLanguage.INSTANCE); //JSONLanguageDialect.JSON
20 | }
21 |
22 | @NotNull
23 | public String getName() {
24 | return "ESLint";
25 | }
26 |
27 | @NotNull
28 | public String getDescription() {
29 | return "ESLint configuration file";
30 | }
31 |
32 | @NotNull
33 | public String getDefaultExtension() {
34 | return ESLINTRC_EXT;
35 | }
36 |
37 | @NotNull
38 | public Icon getIcon() {
39 | return ESLintIcons.ESLint;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/com/eslint/config/ESLintConfigFileTypeFactory.java:
--------------------------------------------------------------------------------
1 | package com.eslint.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 ESLintConfigFileTypeFactory extends FileTypeFactory {
10 | public void createFileTypes(@NotNull FileTypeConsumer consumer) {
11 | consumer.consume(ESLintConfigFileType.INSTANCE, new ExactFileNameMatcher(ESLintConfigFileType.ESLINTRC));
12 | // new ExtensionFileNameMatcher(ESLintConfigFileType.ESLINTRC), new ExactFileNameMatcher("eslint.json"));
13 | }
14 | }
--------------------------------------------------------------------------------
/src/com/eslint/config/ESLintConfigFileUtil.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.intellij.lang.javascript.JSTokenTypes;
5 | import com.intellij.lang.javascript.psi.JSFile;
6 | import com.intellij.lang.javascript.psi.JSObjectLiteralExpression;
7 | import com.intellij.lang.javascript.psi.JSProperty;
8 | import com.intellij.openapi.vfs.VirtualFile;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.util.PsiTreeUtil;
11 | import com.intellij.util.ObjectUtils;
12 | import com.intellij.util.containers.HashSet;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.Collections;
17 | import java.util.Set;
18 |
19 | /**
20 | * @author idok
21 | */
22 | public final class ESLintConfigFileUtil {
23 | private ESLintConfigFileUtil() {
24 | }
25 |
26 | private static final Set FILES = createSet();
27 |
28 | public static boolean isESLintConfigFile(JSFile file) {
29 | return file != null && (isESLintConfigFile(file.getVirtualFile()) || file.getFileType().equals(ESLintConfigFileType.INSTANCE));
30 | }
31 |
32 | private static Set createSet() {
33 | Set s = new HashSet();
34 | Collections.addAll(s, ESLintConfigFileType.ESLINTRC_FILES);
35 | return s;
36 | }
37 |
38 | public static boolean isRC(String fileName) {
39 | return FILES.contains(fileName);
40 | }
41 |
42 | public static boolean isESLintConfigFile(PsiElement position) {
43 | return isESLintConfigFile(position.getContainingFile().getOriginalFile().getVirtualFile());
44 | }
45 |
46 | public static boolean isESLintConfigFile(VirtualFile file) {
47 | // return file != null && file.getName().equals(ESLintConfigFileType.ESLINTRC);
48 | return file != null && isRC(file.getName());
49 | }
50 |
51 | @Nullable
52 | public static JSProperty getProperty(@NotNull PsiElement position) {
53 | JSProperty property = PsiTreeUtil.getParentOfType(position, JSProperty.class, false);
54 | if (property != null) {
55 | JSObjectLiteralExpression objectLiteralExpression = ObjectUtils.tryCast(property.getParent(), JSObjectLiteralExpression.class);
56 | if (objectLiteralExpression != null) {
57 | return property;
58 | }
59 | }
60 | return null;
61 | }
62 |
63 | @Nullable
64 | public static PsiElement getStringLiteral(@NotNull JSProperty property) {
65 | PsiElement firstElement = property.getFirstChild();
66 | if (firstElement != null && isStringLiteral(firstElement)) {
67 | return firstElement;
68 | }
69 | return null;
70 | }
71 |
72 | public static boolean isStringLiteral(@NotNull PsiElement element) {
73 | if (element instanceof ASTNode) {
74 | ASTNode node = (ASTNode) element;
75 | return node.getElementType().equals(JSTokenTypes.STRING_LITERAL);
76 | }
77 | return false;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/com/eslint/config/schema/BaseType.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config.schema;
2 |
3 | public class BaseType {
4 | public String title;
5 | public ESLintSchema.PropertyType type;
6 | public String description;
7 |
8 | public BaseType() {
9 | }
10 |
11 | public static final String ANY_NAME = "*";
12 |
13 | public BaseType(String title, ESLintSchema.PropertyType type, String description) {
14 | this.title = title;
15 | this.type = type;
16 | this.description = description;
17 | }
18 |
19 | public boolean isValidValue(String value) {
20 | return true;
21 | }
22 |
23 | public static boolean isBoolean(String valueStr) {
24 | return Boolean.TRUE.toString().equals(valueStr) || Boolean.FALSE.toString().equals(valueStr);
25 | }
26 |
27 | public static class SchemaBoolean extends BaseType {
28 | public SchemaBoolean() {
29 | type = ESLintSchema.PropertyType.BOOLEAN;
30 | }
31 |
32 | @Override
33 | public boolean isValidValue(String value) {
34 | return isBoolean(value);
35 | }
36 | }
37 |
38 | public static class SchemaAny extends BaseType {
39 | public SchemaAny(String title, String description) {
40 | super(title, ESLintSchema.PropertyType.ANY, description);
41 | }
42 |
43 | public SchemaAny() {
44 | type = ESLintSchema.PropertyType.ANY;
45 | }
46 | }
47 |
48 | public static class SchemaString extends BaseType {
49 | public SchemaString() {
50 | type = ESLintSchema.PropertyType.STRING;
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/com/eslint/config/schema/ESLintSchema.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config.schema;
2 |
3 | import com.google.gson.*;
4 | import com.intellij.util.Function;
5 | import com.intellij.util.containers.ContainerUtil;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.util.List;
11 |
12 | public final class ESLintSchema {
13 | public static final String RULES = "rules";
14 |
15 | // public RootProp properties;
16 |
17 | // public static final SchemaJsonObject instance = load();
18 |
19 | // public static SchemaJsonObject ROOT = new SchemaJsonObject("root", PropertyType.OBJECT, "", new BaseType[]{
20 | // new SchemaJsonObject("env", PropertyType.OBJECT, "env",
21 | // new BaseType[]{
22 | // new BaseType("amd", PropertyType.BOOLEAN, "amd"),
23 | // new BaseType("node", PropertyType.BOOLEAN, "node"),
24 | // new BaseType("browser", PropertyType.BOOLEAN, "browser")
25 | // }
26 | // ),
27 | // new SchemaJsonObject("globals", PropertyType.OBJECT, "globals", new BaseType[]{
28 | // new BaseType(BaseType.ANY_NAME, PropertyType.BOOLEAN, "")
29 | // }),
30 | // new SchemaJsonObject("rules", PropertyType.OBJECT, "rules", new BaseType[]{})
31 | // }
32 | // );
33 | public static SchemaJsonObject ROOT;
34 |
35 | private ESLintSchema() {
36 | }
37 |
38 | public static void buildSchema() {
39 | BaseType rules = ROOT.find(RULES);
40 | if (rules != null) {
41 | List rulesMap = ContainerUtil.map(RuleCache.instance.rulesMap, new Function() {
42 | public BaseType fun(String rule) {
43 | return new BaseType(rule, PropertyType.ANY, rule);
44 | }
45 | });
46 | if (rules instanceof SchemaJsonObject) {
47 | SchemaJsonObject obj = (SchemaJsonObject) rules;
48 | obj.properties = rulesMap.toArray(new BaseType[rulesMap.size()]);
49 | }
50 | }
51 | }
52 |
53 | // public static Gson getGson() {
54 | // // Gson gson = new GsonBuilder().setPrettyPrinting().create();
55 | // GsonBuilder builder = new GsonBuilder();
56 | // builder.registerTypeAdapter(BaseType.class, new BaseTypeAdapter());
57 | // return builder.setPrettyPrinting().create();
58 | // }
59 |
60 | public static Gson getGson() {
61 | RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of(BaseType.class, "type")
62 | .registerSubtype(SchemaJsonObject.class, PropertyType.OBJECT.name())
63 | .registerSubtype(BaseType.SchemaString.class, PropertyType.STRING.name())
64 | .registerSubtype(BaseType.SchemaAny.class, PropertyType.ANY.name())
65 | .registerSubtype(BaseType.SchemaBoolean.class, PropertyType.BOOLEAN.name());
66 | GsonBuilder builder = new GsonBuilder();
67 | builder.registerTypeAdapterFactory(adapter);
68 | return builder.setPrettyPrinting().create();
69 | }
70 |
71 | public static SchemaJsonObject load() {
72 | // FileReader reader = null;
73 | InputStreamReader reader = null;
74 | try {
75 | // reader = new FileReader(schema);
76 | InputStream stream = ESLintSchema.class.getResourceAsStream("/com/eslint/config/schema/schema.json");
77 | // Reader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
78 | reader = new InputStreamReader(stream, "UTF-8");
79 | Gson gson = getGson();
80 | SchemaJsonObject ret = gson.fromJson(reader, SchemaJsonObject.class);
81 | ROOT = ret;
82 | // System.out.println(ret.description);
83 | return ret;
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | } finally {
87 | if (reader != null) {
88 | try {
89 | reader.close();
90 | } catch (IOException e) {
91 | e.printStackTrace();
92 | }
93 | }
94 | }
95 | return null;
96 | }
97 |
98 | // public static class BaseTypeAdapter implements JsonSerializer, JsonDeserializer {
99 | // private static final String CLASSNAME = "CLASSNAME";
100 | // private static final String INSTANCE = "INSTANCE";
101 | // private static final String TYPE = "class-type";
102 | //
103 | // @Override
104 | // public JsonElement serialize(BaseType src, Type typeOfSrc, JsonSerializationContext context) {
105 | // JsonObject retValue = new JsonObject();
106 | // String className = src.getClass().getCanonicalName();
107 | // retValue.addProperty(CLASSNAME, className);
108 | // JsonElement elem = context.serialize(src);
109 | // elem.getAsJsonObject().addProperty(TYPE, className);
110 | // retValue.add(INSTANCE, elem);
111 | // return retValue;
112 | // }
113 | //
114 | // @Override
115 | // public BaseType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
116 | // JsonObject jsonObject = json.getAsJsonObject();
117 | // JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
118 | // String className = prim.getAsString();
119 | //
120 | // Class> klass;
121 | // try {
122 | // klass = Class.forName(className);
123 | // } catch (ClassNotFoundException e) {
124 | // e.printStackTrace();
125 | // throw new JsonParseException(e.getMessage());
126 | // }
127 | // return context.deserialize(jsonObject.get(INSTANCE), klass);
128 | // }
129 | // }
130 |
131 | // public static class BookTypeAdapter extends TypeAdapter {
132 | // @Override
133 | // public BaseType read(final JsonReader in) throws IOException {
134 | // final BaseType book = new BaseType();
135 | // in.beginObject();
136 | // while (in.hasNext()) {
137 | // switch (in.nextName()) {
138 | // case "isbn":
139 | // super.read(in);
140 | // book.setIsbn(in.nextString());
141 | // break;
142 | // case "title":
143 | // book.setTitle(in.nextString());
144 | // break;
145 | // case "authors":
146 | // book.setAuthors(in.nextString().split(";"));
147 | // break;
148 | // }
149 | // }
150 | // in.endObject();
151 | // return book;
152 | // }
153 | //
154 | // @Override
155 | // public void write(final JsonWriter out, final BaseType book) throws IOException {
156 | // out.beginObject();
157 | // out.name("isbn").value(book.getIsbn());
158 | // out.name("title").value(book.getTitle());
159 | // out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));
160 | // out.endObject();
161 | // }
162 | // }
163 |
164 | // public static class PropertyTypeAdapter implements JsonSerializer, JsonDeserializer {
165 | // private static final String CLASSNAME = "CLASSNAME";
166 | // private static final String INSTANCE = "INSTANCE";
167 | // private static final String TYPE = "type";
168 | //
169 | // @Override
170 | // public JsonElement serialize(PropertyType src, Type typeOfSrc, JsonSerializationContext context) {
171 | //// JsonObject retValue = new JsonObject();
172 | // String className = src.getClass().getCanonicalName();
173 | //// retValue.addProperty(CLASSNAME, className);
174 | // JsonElement elem = context.serialize(src);
175 | // elem.getAsJsonObject().addProperty(TYPE, className);
176 | //// retValue.add(INSTANCE, elem);
177 | // return elem;
178 | // }
179 | //
180 | // @Override
181 | // public PropertyType deserialize(PropertyType json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
182 | // JsonObject jsonObject = json.getAsJsonObject();
183 | // JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
184 | // String className = prim.getAsString();
185 | //
186 | // Class> klass;
187 | // try {
188 | // klass = Class.forName(className);
189 | // } catch (ClassNotFoundException e) {
190 | // e.printStackTrace();
191 | // throw new JsonParseException(e.getMessage());
192 | // }
193 | // return context.deserialize(jsonObject.get(INSTANCE), klass);
194 | // }
195 | // }
196 |
197 | public enum PropertyType {ANY, BOOLEAN, STRING, OBJECT, INT}
198 | }
199 |
--------------------------------------------------------------------------------
/src/com/eslint/config/schema/RuleCache.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config.schema;
2 |
3 | import com.eslint.ESLintProjectComponent;
4 | import com.google.common.io.Files;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.text.StringUtil;
7 | import com.intellij.util.Function;
8 | import com.intellij.util.containers.ContainerUtil;
9 | import com.wix.utils.FileUtils;
10 |
11 | import java.io.File;
12 | import java.io.FilenameFilter;
13 | import java.util.ArrayList;
14 | import java.util.Arrays;
15 | import java.util.List;
16 | import java.util.Set;
17 |
18 | // TODO refresh when config change
19 | public final class RuleCache {
20 |
21 | public List rules = new ArrayList();
22 |
23 | public Set rulesMap = ContainerUtil.newLinkedHashSet();
24 |
25 | public static RuleCache instance;
26 |
27 | public void read(String path) {
28 | FilenameFilter filter = new FilenameFilter() {
29 | @Override
30 | public boolean accept(File file, String name) {
31 | return name.endsWith(".js");
32 | }
33 | };
34 | String[] rules1 = new File(path).list(filter);
35 | rules.addAll(Arrays.asList(rules1));
36 |
37 | List names = ContainerUtil.map(rules1, new Function() {
38 | public String fun(String file) {
39 | return Files.getNameWithoutExtension(file);
40 | }
41 | });
42 | rulesMap.addAll(names);
43 | }
44 |
45 | public void readRules() {
46 | SchemaJsonObject schemaRules = ESLintSchema.ROOT.findOfType(ESLintSchema.RULES);
47 | if (schemaRules != null) {
48 | List tempRules = ContainerUtil.map(rulesMap, new Function() {
49 | @Override
50 | public BaseType.SchemaAny fun(String ruleName) {
51 | return new BaseType.SchemaAny(ruleName, ruleName);
52 | }
53 | });
54 | schemaRules.properties = tempRules.toArray(new BaseType[tempRules.size()]);
55 | }
56 | }
57 |
58 | // private static void initialize(Project project, String builtinRulesPath) {
59 | // instance = new RuleCache();
60 | // ESLintSchema.load();
61 | // SchemaJsonObject rules = ESLintSchema.ROOT.findOfType(ESLintSchema.RULES);
62 | // if (rules != null) {
63 | // for (BaseType b : rules.properties) {
64 | // RuleCache.instance.rulesMap.add(b.title);
65 | // }
66 | // }
67 | // String absRulesPath = FileUtils.resolvePath(project, builtinRulesPath);
68 | // if (StringUtil.isNotEmpty(absRulesPath)) {
69 | // instance.read(absRulesPath);
70 | // }
71 | //// instance.read(RuleCache.defaultPath);
72 | // }
73 |
74 | // public static void initializeFromPath(Project project, String builtinRulesPath) {
75 | // instance = new RuleCache();
76 | // String absRulesPath = FileUtils.resolvePath(project, builtinRulesPath);
77 | // if (StringUtil.isNotEmpty(absRulesPath)) {
78 | // instance.read(absRulesPath);
79 | // }
80 | // instance.read(RuleCache.defaultPath);
81 | // }
82 |
83 | // public static void initializeFromPath(Project project) {
84 | // ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
85 | // instance = new RuleCache();
86 | // ESLintSchema.load();
87 | // String absRulesPath = FileUtils.resolvePath(project, component.rulesPath);
88 | // if (StringUtil.isNotEmpty(absRulesPath)) {
89 | // instance.read(absRulesPath);
90 | // }
91 | // instance.read(component.builtinRulesPath);
92 | // }
93 |
94 | public static void initializeFromPath(Project project, ESLintProjectComponent component) {
95 | // ESLintProjectComponent component = project.getComponent(ESLintProjectComponent.class);
96 | String absRulesPath = FileUtils.resolvePath(project, component.customRulesPath);
97 | initializeFromPaths(component.rulesPath, absRulesPath);
98 | }
99 |
100 | private static void initializeFromPaths(String... paths) {
101 | instance = new RuleCache();
102 | ESLintSchema.load();
103 | for (String path : paths) {
104 | if (StringUtil.isNotEmpty(path)) {
105 | instance.read(path);
106 | }
107 | }
108 | instance.readRules();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/com/eslint/config/schema/RuntimeTypeAdapterFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.eslint.config.schema;
18 |
19 | import java.io.IOException;
20 | import java.util.LinkedHashMap;
21 | import java.util.Map;
22 |
23 | import com.google.gson.Gson;
24 | import com.google.gson.JsonElement;
25 | import com.google.gson.JsonObject;
26 | import com.google.gson.JsonParseException;
27 | import com.google.gson.JsonPrimitive;
28 | import com.google.gson.TypeAdapter;
29 | import com.google.gson.TypeAdapterFactory;
30 | import com.google.gson.internal.Streams;
31 | import com.google.gson.reflect.TypeToken;
32 | import com.google.gson.stream.JsonReader;
33 | import com.google.gson.stream.JsonWriter;
34 |
35 | /**
36 | * Adapts values whose runtime type may differ from their declaration type. This
37 | * is necessary when a field's type is not the same type that GSON should create
38 | * when deserializing that field. For example, consider these types:
39 | * {@code
40 | * abstract class Shape {
41 | * int x;
42 | * int y;
43 | * }
44 | * class Circle extends Shape {
45 | * int radius;
46 | * }
47 | * class Rectangle extends Shape {
48 | * int width;
49 | * int height;
50 | * }
51 | * class Diamond extends Shape {
52 | * int width;
53 | * int height;
54 | * }
55 | * class Drawing {
56 | * Shape bottomShape;
57 | * Shape topShape;
58 | * }
59 | * }
60 | * Without additional type information, the serialized JSON is ambiguous. Is
61 | * the bottom shape in this drawing a rectangle or a diamond?
{@code
62 | * {
63 | * "bottomShape": {
64 | * "width": 10,
65 | * "height": 5,
66 | * "x": 0,
67 | * "y": 0
68 | * },
69 | * "topShape": {
70 | * "radius": 2,
71 | * "x": 4,
72 | * "y": 1
73 | * }
74 | * }}
75 | * This class addresses this problem by adding type information to the
76 | * serialized JSON and honoring that type information when the JSON is
77 | * deserialized: {@code
78 | * {
79 | * "bottomShape": {
80 | * "type": "Diamond",
81 | * "width": 10,
82 | * "height": 5,
83 | * "x": 0,
84 | * "y": 0
85 | * },
86 | * "topShape": {
87 | * "type": "Circle",
88 | * "radius": 2,
89 | * "x": 4,
90 | * "y": 1
91 | * }
92 | * }}
93 | * Both the type field name ({@code "type"}) and the type labels ({@code
94 | * "Rectangle"}) are configurable.
95 | *
96 | * Registering Types
97 | * Create a {@code RuntimeTypeAdapter} by passing the base type and type field
98 | * name to the {@link #of} factory method. If you don't supply an explicit type
99 | * field name, {@code "type"} will be used. {@code
100 | * RuntimeTypeAdapter shapeAdapter
101 | * = RuntimeTypeAdapter.of(Shape.class, "type");
102 | * }
103 | * Next register all of your subtypes. Every subtype must be explicitly
104 | * registered. This protects your application from injection attacks. If you
105 | * don't supply an explicit type label, the type's simple name will be used.
106 | * {@code
107 | * shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
108 | * shapeAdapter.registerSubtype(Circle.class, "Circle");
109 | * shapeAdapter.registerSubtype(Diamond.class, "Diamond");
110 | * }
111 | * Finally, register the type adapter in your application's GSON builder:
112 | * {@code
113 | * Gson gson = new GsonBuilder()
114 | * .registerTypeAdapter(Shape.class, shapeAdapter)
115 | * .create();
116 | * }
117 | * Like {@code GsonBuilder}, this API supports chaining: {@code
118 | * RuntimeTypeAdapter shapeAdapter = RuntimeTypeAdapterFactory.of(Shape.class)
119 | * .registerSubtype(Rectangle.class)
120 | * .registerSubtype(Circle.class)
121 | * .registerSubtype(Diamond.class);
122 | * }
123 | */
124 | public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
125 | private final Class> baseType;
126 | private final String typeFieldName;
127 | private final Map> labelToSubtype = new LinkedHashMap>();
128 | private final Map, String> subtypeToLabel = new LinkedHashMap, String>();
129 |
130 | private RuntimeTypeAdapterFactory(Class> baseType, String typeFieldName) {
131 | if (typeFieldName == null || baseType == null) {
132 | throw new NullPointerException();
133 | }
134 | this.baseType = baseType;
135 | this.typeFieldName = typeFieldName;
136 | }
137 |
138 | /**
139 | * Creates a new runtime type adapter using for {@code baseType} using {@code
140 | * typeFieldName} as the type field name. Type field names are case sensitive.
141 | */
142 | public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) {
143 | return new RuntimeTypeAdapterFactory(baseType, typeFieldName);
144 | }
145 |
146 | /**
147 | * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
148 | * the type field name.
149 | */
150 | public static RuntimeTypeAdapterFactory of(Class baseType) {
151 | return new RuntimeTypeAdapterFactory(baseType, "type");
152 | }
153 |
154 | /**
155 | * Registers {@code type} identified by {@code label}. Labels are case
156 | * sensitive.
157 | *
158 | * @throws IllegalArgumentException if either {@code type} or {@code label}
159 | * have already been registered on this type adapter.
160 | */
161 | public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type, String label) {
162 | if (type == null || label == null) {
163 | throw new NullPointerException();
164 | }
165 | if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
166 | throw new IllegalArgumentException("types and labels must be unique");
167 | }
168 | labelToSubtype.put(label, type);
169 | subtypeToLabel.put(type, label);
170 | return this;
171 | }
172 |
173 | /**
174 | * Registers {@code type} identified by its {@link Class#getSimpleName simple
175 | * name}. Labels are case sensitive.
176 | *
177 | * @throws IllegalArgumentException if either {@code type} or its simple name
178 | * have already been registered on this type adapter.
179 | */
180 | public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type) {
181 | return registerSubtype(type, type.getSimpleName());
182 | }
183 |
184 | public TypeAdapter create(Gson gson, TypeToken type) {
185 | if (type.getRawType() != baseType) {
186 | return null;
187 | }
188 |
189 | final Map> labelToDelegate
190 | = new LinkedHashMap>();
191 | final Map, TypeAdapter>> subtypeToDelegate
192 | = new LinkedHashMap, TypeAdapter>>();
193 | for (Map.Entry> entry : labelToSubtype.entrySet()) {
194 | TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
195 | labelToDelegate.put(entry.getKey(), delegate);
196 | subtypeToDelegate.put(entry.getValue(), delegate);
197 | }
198 |
199 | return new TypeAdapter() {
200 | @Override public R read(JsonReader in) throws IOException {
201 | JsonElement jsonElement = Streams.parse(in);
202 | JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
203 | if (labelJsonElement == null) {
204 | throw new JsonParseException("cannot deserialize " + baseType
205 | + " because it does not define a field named " + typeFieldName);
206 | }
207 | String label = labelJsonElement.getAsString();
208 | @SuppressWarnings("unchecked") // registration requires that subtype extends T
209 | TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label);
210 | if (delegate == null) {
211 | throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
212 | + label + "; did you forget to register a subtype?");
213 | }
214 | return delegate.fromJsonTree(jsonElement);
215 | }
216 |
217 | @Override public void write(JsonWriter out, R value) throws IOException {
218 | Class> srcType = value.getClass();
219 | String label = subtypeToLabel.get(srcType);
220 | @SuppressWarnings("unchecked") // registration requires that subtype extends T
221 | TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType);
222 | if (delegate == null) {
223 | throw new JsonParseException("cannot serialize " + srcType.getName()
224 | + "; did you forget to register a subtype?");
225 | }
226 | JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
227 | if (jsonObject.has(typeFieldName)) {
228 | throw new JsonParseException("cannot serialize " + srcType.getName()
229 | + " because it already defines a field named " + typeFieldName);
230 | }
231 | JsonObject clone = new JsonObject();
232 | clone.add(typeFieldName, new JsonPrimitive(label));
233 | for (Map.Entry e : jsonObject.entrySet()) {
234 | clone.add(e.getKey(), e.getValue());
235 | }
236 | Streams.write(clone, out);
237 | }
238 | };
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/com/eslint/config/schema/SchemaJsonObject.java:
--------------------------------------------------------------------------------
1 | package com.eslint.config.schema;
2 |
3 | public class SchemaJsonObject extends BaseType {
4 | public BaseType[] properties;
5 |
6 | public SchemaJsonObject() {
7 | type = ESLintSchema.PropertyType.OBJECT;
8 | }
9 |
10 | public SchemaJsonObject(String title, ESLintSchema.PropertyType type, String description, BaseType[] properties) {
11 | super(title, type, description);
12 | this.properties = properties;
13 | }
14 |
15 | public BaseType find(String name) {
16 | for (BaseType b : properties) {
17 | if (b.title.equals(name)) {
18 | return b;
19 | }
20 | }
21 | return null;
22 | }
23 |
24 | public T findOfType(String name) {
25 | for (BaseType b : properties) {
26 | if (b.title.equals(name)) {
27 | // if (b instanceof T) {
28 | return (T) b;
29 | // }
30 | }
31 | }
32 | return null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/BaseActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
4 | import com.intellij.codeInsight.intention.HighPriorityAction;
5 | import com.intellij.codeInsight.intention.IntentionAction;
6 | import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.PsiFile;
11 | import com.intellij.util.IncorrectOperationException;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | /**
16 | * @author idok
17 | */
18 | public abstract class BaseActionFix extends LocalQuickFixAndIntentionActionOnPsiElement implements IntentionAction, HighPriorityAction {
19 | public BaseActionFix(PsiElement element) {
20 | super(element);
21 | }
22 |
23 | @NotNull
24 | @Override
25 | public String getFamilyName() {
26 | return getText();
27 | }
28 |
29 | // @Override
30 | // public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
31 | // return true;
32 | // }
33 |
34 | // protected abstract void fix(@NotNull Project project, Editor editor, PsiFile file, PsiElement start);
35 |
36 | // @Override
37 | // public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
38 | // fix(project, editor, file);
39 | // DaemonCodeAnalyzer.getInstance(project).restart(file);
40 | // }
41 |
42 | // public void invoke(@NotNull Project project, @NotNull PsiFile file, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement start, @NotNull PsiElement end) {
43 | // invoke(project, editor, file, start);
44 | // }
45 |
46 | @Override
47 | public boolean startInWriteAction() {
48 | return true;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/DotNotationActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
5 | import com.intellij.lang.ASTNode;
6 | import com.intellij.lang.javascript.psi.*;
7 | import com.intellij.lang.javascript.psi.impl.JSChangeUtil;
8 | import com.intellij.openapi.editor.Editor;
9 | import com.intellij.openapi.project.Project;
10 | import com.intellij.openapi.util.text.StringUtil;
11 | import com.intellij.psi.PsiElement;
12 | import com.intellij.psi.PsiFile;
13 | import com.intellij.psi.util.PsiTreeUtil;
14 | import com.intellij.util.IncorrectOperationException;
15 | import org.jetbrains.annotations.Nls;
16 | import org.jetbrains.annotations.NotNull;
17 | import org.jetbrains.annotations.Nullable;
18 |
19 | /**
20 | * @author idok
21 | */
22 | public class DotNotationActionFix extends BaseActionFix {
23 | public DotNotationActionFix(PsiElement element) {
24 | super(element);
25 | }
26 |
27 | @NotNull
28 | @Override
29 | public String getText() {
30 | return ESLintBundle.message("inspection.fix.dot-notation");
31 | }
32 |
33 | @Override
34 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
35 | JSIndexedPropertyAccessExpression indexed = PsiTreeUtil.getParentOfType(element, JSIndexedPropertyAccessExpression.class);
36 | JSReferenceExpression ref = PsiTreeUtil.findChildOfType(indexed, JSReferenceExpression.class);
37 | JSLiteralExpression literalExpression = (JSLiteralExpression) indexed.getIndexExpression();
38 | String path = StringUtil.stripQuotesAroundValue(literalExpression.getText());
39 | ASTNode dotExp = JSChangeUtil.createStatementFromText(project, ref.getText() + '.' + path);
40 | indexed.replace(dotExp.getPsi());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/EqeqeqActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.eslint.utils.JSBinaryExpressionUtil;
5 | import com.intellij.lang.ASTNode;
6 | import com.intellij.openapi.editor.Document;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.psi.PsiDocumentManager;
10 | import com.intellij.psi.PsiElement;
11 | import com.intellij.psi.PsiFile;
12 | import com.intellij.util.IncorrectOperationException;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | /**
17 | * @author idok
18 | */
19 | public class EqeqeqActionFix extends BaseActionFix {
20 |
21 | public EqeqeqActionFix(PsiElement element) {
22 | super(element);
23 | }
24 |
25 | @NotNull
26 | @Override
27 | public String getText() {
28 | return ESLintBundle.message("inspection.fix.eqeqeq");
29 | }
30 |
31 | @Override
32 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
33 | ASTNode op = JSBinaryExpressionUtil.getOperator(element);
34 | Document document = PsiDocumentManager.getInstance(project).getDocument(element.getContainingFile());
35 |
36 | String replace = "";
37 | if (op.getText().equals("==")) {
38 | replace = "===";
39 | } else if (op.getText().equals("!=")) {
40 | replace = "!==";
41 | }
42 | document.replaceString(op.getStartOffset(), op.getStartOffset() + op.getTextLength(), replace);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/Fixes.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.google.common.base.Strings;
4 | import com.intellij.psi.PsiElement;
5 |
6 | public final class Fixes {
7 | private Fixes() {
8 | }
9 |
10 | public static BaseActionFix getFixForRule(String rule, PsiElement element) {
11 | // Map map = new HashMap();
12 | // map.put("strict", )
13 | if (Strings.isNullOrEmpty(rule)) {
14 | return null;
15 | }
16 | if (rule.equals("strict")) {
17 | return new StrictActionFix(element);
18 | }
19 | if (rule.equals("no-new-object")) {
20 | return new NoNewObjectActionFix(element);
21 | }
22 | if (rule.equals("no-array-constructor")) {
23 | return new NoArrayConstructorActionFix(element);
24 | }
25 | if (rule.equals("eqeqeq")) {
26 | return new EqeqeqActionFix(element);
27 | }
28 | if (rule.equals("no-negated-in-lhs")) {
29 | return new NoNegatedInLhsActionFix(element);
30 | }
31 | if (rule.equals("no-lonely-if")) {
32 | return new NoLonelyIfActionFix(element);
33 | }
34 | if (rule.equals("dot-notation")) {
35 | return new DotNotationActionFix(element);
36 | }
37 | return null;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/com/eslint/fixes/NoArrayConstructorActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.lang.javascript.psi.JSElementFactory;
5 | import com.intellij.lang.javascript.psi.JSExpressionCodeFragment;
6 | import com.intellij.lang.javascript.psi.JSNewExpression;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.PsiFile;
11 | import com.intellij.util.IncorrectOperationException;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | /**
15 | * @author idok
16 | */
17 | public class NoArrayConstructorActionFix extends NoNewBaseActionFix {
18 | public NoArrayConstructorActionFix(PsiElement element) {
19 | super(element);
20 | }
21 |
22 | @Override
23 | protected String getNewExp() {
24 | return "[]";
25 | }
26 |
27 | @NotNull
28 | @Override
29 | public String getText() {
30 | return ESLintBundle.message("inspection.fix.no-array-constructor");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/NoLonelyIfActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.lang.javascript.psi.JSIfStatement;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.psi.PsiElement;
8 | import com.intellij.psi.PsiFile;
9 | import com.intellij.psi.util.PsiTreeUtil;
10 | import com.intellij.util.IncorrectOperationException;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | /**
15 | * @author idok
16 | */
17 | public class NoLonelyIfActionFix extends BaseActionFix {
18 | public NoLonelyIfActionFix(PsiElement element) {
19 | super(element);
20 | }
21 |
22 | @NotNull
23 | @Override
24 | public String getText() {
25 | return ESLintBundle.message("inspection.fix.no-lonely-if");
26 | }
27 |
28 | @Override
29 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
30 | JSIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, JSIfStatement.class);
31 | JSIfStatement parentIf = PsiTreeUtil.getParentOfType(ifStatement, JSIfStatement.class);
32 | parentIf.getElse().replace(ifStatement);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/NoNegatedInLhsActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.lang.ASTNode;
5 | import com.intellij.lang.javascript.psi.JSBinaryExpression;
6 | import com.intellij.lang.javascript.psi.JSParenthesizedExpression;
7 | import com.intellij.lang.javascript.psi.impl.JSChangeUtil;
8 | import com.intellij.openapi.editor.Editor;
9 | import com.intellij.openapi.project.Project;
10 | import com.intellij.psi.PsiElement;
11 | import com.intellij.psi.PsiFile;
12 | import com.intellij.psi.util.PsiTreeUtil;
13 | import com.intellij.util.IncorrectOperationException;
14 | import org.jetbrains.annotations.NotNull;
15 | import org.jetbrains.annotations.Nullable;
16 |
17 | /**
18 | * @author idok
19 | */
20 | public class NoNegatedInLhsActionFix extends BaseActionFix {
21 | public NoNegatedInLhsActionFix(PsiElement element) {
22 | super(element);
23 | }
24 |
25 | @NotNull
26 | @Override
27 | public String getText() {
28 | return ESLintBundle.message("inspection.fix.no-negated-in-lhs");
29 | }
30 |
31 | @Override
32 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
33 | // PsiElement element = descriptor.getPsiElement();
34 | JSBinaryExpression binary = PsiTreeUtil.getParentOfType(element, JSBinaryExpression.class);
35 | JSBinaryExpression binaryClone = (JSBinaryExpression) binary.copy();
36 | binaryClone.getLOperand().replace(binary.getLOperand().getLastChild());
37 | ASTNode negate = JSChangeUtil.createStatementFromText(project, "!(true)");
38 | JSParenthesizedExpression paren = PsiTreeUtil.getChildOfType(negate.getPsi().getFirstChild(), JSParenthesizedExpression.class);
39 | paren.getInnerExpression().replace(binaryClone);
40 | binary.replace(negate.getPsi());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/NoNewBaseActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.intellij.lang.javascript.psi.JSElementFactory;
4 | import com.intellij.lang.javascript.psi.JSExpressionCodeFragment;
5 | import com.intellij.lang.javascript.psi.JSNewExpression;
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 com.intellij.util.IncorrectOperationException;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | /**
15 | * @author idok
16 | */
17 | public abstract class NoNewBaseActionFix extends BaseActionFix {
18 | protected NoNewBaseActionFix(PsiElement element) {
19 | super(element);
20 | }
21 |
22 | protected abstract String getNewExp();
23 |
24 | @Override
25 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
26 | PsiElement parent = element.getParent();
27 | if (!(parent instanceof JSNewExpression)) return;
28 | final JSExpressionCodeFragment useStrict = JSElementFactory.createExpressionCodeFragment(project, getNewExp(), parent);
29 | PsiElement child = useStrict.getFirstChild();
30 | parent.replace(child);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/NoNewObjectActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.lang.javascript.psi.JSElementFactory;
5 | import com.intellij.lang.javascript.psi.JSExpressionCodeFragment;
6 | import com.intellij.lang.javascript.psi.JSNewExpression;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.PsiFile;
11 | import com.intellij.util.IncorrectOperationException;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | /**
15 | * @author idok
16 | */
17 | public class NoNewObjectActionFix extends NoNewBaseActionFix {
18 | public NoNewObjectActionFix(PsiElement element) {
19 | super(element);
20 | }
21 |
22 | @Override
23 | protected String getNewExp() {
24 | return "{}";
25 | }
26 |
27 | @NotNull
28 | @Override
29 | public String getText() {
30 | return ESLintBundle.message("inspection.fix.no.new.object");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/StrictActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.lang.javascript.psi.*;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.util.TextRange;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.util.IncorrectOperationException;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | /**
15 | * @author idok
16 | */
17 | public class StrictActionFix extends BaseActionFix {
18 | public StrictActionFix(PsiElement element) {
19 | super(element);
20 | }
21 |
22 | @NotNull
23 | @Override
24 | public String getText() {
25 | return ESLintBundle.message("inspection.fix.strict");
26 | }
27 |
28 | @Override
29 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement element, @NotNull PsiElement end) throws IncorrectOperationException {
30 | final PsiElement parent = element.getParent();
31 | if (!(parent instanceof JSFunctionExpression || parent instanceof JSFunction)) return;
32 |
33 | // if (parent.getChildren().length < 2) {
34 | // return;
35 | // }
36 |
37 | JSBlockStatement block = null;
38 | for (PsiElement elem : parent.getChildren()) {
39 | if (elem instanceof JSBlockStatement) {
40 | block = (JSBlockStatement) elem;
41 | break;
42 | }
43 | }
44 |
45 | if (block != null) {
46 | TextRange textRange = block.getTextRange();
47 |
48 | // PsiTreeUtil. JSPsiImplUtils
49 | final JSExpressionCodeFragment useStrict = JSElementFactory.createExpressionCodeFragment(project, "'use strict';\n", block);
50 | PsiElement child = useStrict.getFirstChild();
51 | if (block.getStatements().length == 0) {
52 | block.add(child);
53 | } else {
54 | block.addBefore(child, block.getStatements()[0]);
55 | }
56 | // final JSBlockStatement finalBlock = block;
57 | // ApplicationManager.getApplication().runWriteAction(new Runnable() {
58 | // @Override
59 | // public void run() {
60 | // finalBlock.add(useStrict);
61 | // }
62 | // });
63 |
64 | // if (textRange != null) {
65 | // Document document = PsiDocumentManager.getInstance(project).getDocument(element.getContainingFile());
66 | //// TextRange docRange = textRange.shiftRight(element.getTextRange().getStartOffset());
67 | // document.insertString(textRange.getStartOffset() + 1, "\n'use strict';");
68 | // }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/SuppressActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.intellij.codeInsight.FileModificationService;
4 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
5 | import com.intellij.codeInsight.intention.IntentionAction;
6 | import com.intellij.lang.javascript.psi.JSElement;
7 | import com.intellij.openapi.diagnostic.Logger;
8 | import com.intellij.openapi.editor.Document;
9 | import com.intellij.openapi.editor.Editor;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.psi.PsiDocumentManager;
12 | import com.intellij.psi.PsiElement;
13 | import com.intellij.psi.PsiFile;
14 | import com.intellij.psi.util.PsiTreeUtil;
15 | import com.intellij.util.IncorrectOperationException;
16 | import org.jetbrains.annotations.NonNls;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | /**
20 | * @author idok
21 | */
22 | public class SuppressActionFix implements IntentionAction {
23 | private static final Logger LOG = Logger.getInstance(SuppressActionFix.class);
24 | private final PsiElement element;
25 | private final String rule;
26 |
27 |
28 | public SuppressActionFix(String rule, PsiElement element) {
29 | this.element = element;
30 | this.rule = rule;
31 | }
32 |
33 | @NotNull
34 | @Override
35 | public String getText() {
36 | return "Suppress ESLint rule";
37 | }
38 |
39 | @NotNull
40 | @Override
41 | public String getFamilyName() {
42 | return "ESLint";
43 | }
44 |
45 | @Override
46 | public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
47 | return true;
48 | }
49 |
50 | @Override
51 | public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
52 | // final PsiFile file = element.getContainingFile();
53 | if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
54 |
55 | // InspectionManager inspectionManager = InspectionManager.getInstance(project);
56 | // ProblemDescriptor descriptor = inspectionManager.createProblemDescriptor(element, element, "", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
57 |
58 | final JSElement property = PsiTreeUtil.getParentOfType(element, JSElement.class);
59 | LOG.assertTrue(property != null);
60 | final int start = property.getTextRange().getStartOffset();
61 |
62 | @NonNls final Document doc = PsiDocumentManager.getInstance(project).getDocument(file);
63 | LOG.assertTrue(doc != null);
64 | final int line = doc.getLineNumber(start);
65 | final int lineStart = doc.getLineStartOffset(line);
66 |
67 | doc.insertString(lineStart, "/*eslint " + rule + ":0*/\n");
68 | DaemonCodeAnalyzer.getInstance(project).restart(file);
69 | }
70 |
71 | @Override
72 | public boolean startInWriteAction() {
73 | return true;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/com/eslint/fixes/SuppressLineActionFix.java:
--------------------------------------------------------------------------------
1 | package com.eslint.fixes;
2 |
3 | import com.intellij.codeInsight.FileModificationService;
4 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
5 | import com.intellij.codeInsight.intention.IntentionAction;
6 | import com.intellij.lang.javascript.psi.JSElement;
7 | import com.intellij.openapi.diagnostic.Logger;
8 | import com.intellij.openapi.editor.Document;
9 | import com.intellij.openapi.editor.Editor;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.psi.PsiDocumentManager;
12 | import com.intellij.psi.PsiElement;
13 | import com.intellij.psi.PsiFile;
14 | import com.intellij.psi.util.PsiTreeUtil;
15 | import com.intellij.util.IncorrectOperationException;
16 | import org.jetbrains.annotations.NonNls;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | /**
20 | * @author idok
21 | */
22 | public class SuppressLineActionFix implements IntentionAction {
23 | private static final Logger LOG = Logger.getInstance(SuppressLineActionFix.class);
24 | private final PsiElement element;
25 | private final String rule;
26 |
27 |
28 | public SuppressLineActionFix(String rule, PsiElement element) {
29 | this.element = element;
30 | this.rule = rule;
31 | }
32 |
33 | @NotNull
34 | @Override
35 | public String getText() {
36 | //noinspection DialogTitleCapitalization
37 | return "Suppress ESLint rule line";
38 | }
39 |
40 | @NotNull
41 | @Override
42 | public String getFamilyName() {
43 | return "ESLint";
44 | }
45 |
46 | @Override
47 | public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
48 | return true;
49 | }
50 |
51 | @Override
52 | public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
53 | // final PsiFile file = element.getContainingFile();
54 | if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
55 |
56 | // InspectionManager inspectionManager = InspectionManager.getInstance(project);
57 | // ProblemDescriptor descriptor = inspectionManager.createProblemDescriptor(element, element, "", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
58 |
59 | final JSElement property = PsiTreeUtil.getParentOfType(element, JSElement.class);
60 | LOG.assertTrue(property != null);
61 | final int start = property.getTextRange().getStartOffset();
62 |
63 | @NonNls final Document doc = PsiDocumentManager.getInstance(project).getDocument(file);
64 | LOG.assertTrue(doc != null);
65 | final int line = doc.getLineNumber(start);
66 | final int lineEnd = doc.getLineEndOffset(line);
67 | doc.insertString(lineEnd, " //eslint-disable-line " + rule);
68 | DaemonCodeAnalyzer.getInstance(project).restart(file);
69 | }
70 |
71 | @Override
72 | public boolean startInWriteAction() {
73 | return true;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/com/eslint/inspection/PropertySuppressableInspectionBase.java:
--------------------------------------------------------------------------------
1 | package com.eslint.inspection;
2 |
3 | import com.eslint.ESLintBundle;
4 | import com.intellij.codeInsight.FileModificationService;
5 | import com.intellij.codeInsight.daemon.HighlightDisplayKey;
6 | import com.intellij.codeInsight.daemon.impl.actions.SuppressByCommentFix;
7 | import com.intellij.codeInspection.CustomSuppressableInspectionTool;
8 | import com.intellij.codeInspection.LocalInspectionTool;
9 | import com.intellij.codeInspection.SuppressIntentionAction;
10 | import com.intellij.codeInspection.SuppressQuickFix;
11 | import com.intellij.lang.javascript.inspections.JSInspectionSuppressor;
12 | import com.intellij.lang.javascript.linter.jshint.JSHintInspection;
13 | import com.intellij.lang.javascript.psi.JSElement;
14 | import com.intellij.lang.javascript.psi.JSFile;
15 | import com.intellij.lang.javascript.psi.impl.JSFileImpl;
16 | import com.intellij.openapi.command.CommandProcessor;
17 | import com.intellij.openapi.diagnostic.Logger;
18 | import com.intellij.openapi.editor.Document;
19 | import com.intellij.openapi.editor.Editor;
20 | import com.intellij.openapi.project.Project;
21 | import com.intellij.psi.PsiDocumentManager;
22 | import com.intellij.psi.PsiElement;
23 | import com.intellij.psi.PsiFile;
24 | import com.intellij.psi.PsiNamedElement;
25 | import com.intellij.psi.util.PsiTreeUtil;
26 | import com.intellij.util.IncorrectOperationException;
27 | import org.jetbrains.annotations.NonNls;
28 | import org.jetbrains.annotations.NotNull;
29 | import org.jetbrains.annotations.Nullable;
30 |
31 | public abstract class PropertySuppressableInspectionBase extends LocalInspectionTool { //implements CustomSuppressableInspectionTool {
32 | private static final Logger LOG = Logger.getInstance("#com.intellij.lang.properties.PropertySuppressableInspectionBase");
33 |
34 | @NotNull
35 | public String getGroupDisplayName() {
36 | return ESLintBundle.message("eslint.inspection.group.name");
37 | }
38 |
39 | public SuppressIntentionAction[] getSuppressActions(final PsiElement element) {
40 | PsiNamedElement pe = getProblemElement(element);
41 | return new SuppressIntentionAction[]{new SuppressForStatement(getShortName()), new SuppressForFile(getShortName())};
42 | }
43 |
44 | @NotNull
45 | public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) {
46 | return new SuppressQuickFix[]{new ESLintSuppressByCommentFix(HighlightDisplayKey.find(this.getShortName()), JSInspectionSuppressor.getHolderClass(element))};
47 | }
48 |
49 | public static class ESLintSuppressByCommentFix extends SuppressByCommentFix {
50 | public ESLintSuppressByCommentFix(HighlightDisplayKey key, Class extends PsiElement> suppressionHolderClass) {
51 | super(key, suppressionHolderClass);
52 | }
53 |
54 | @NotNull
55 | public String getText() {
56 | return "Suppress for line";
57 | }
58 |
59 | protected void createSuppression(@NotNull Project project, @NotNull PsiElement element, @NotNull PsiElement container) throws IncorrectOperationException {
60 | if (element.isValid()) {
61 | PsiFile psiFile = element.getContainingFile();
62 | if (psiFile != null) {
63 | psiFile = psiFile.getOriginalFile();
64 | }
65 |
66 | if (psiFile != null && psiFile.isValid()) {
67 | final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
68 | if (document != null) {
69 | int lineNo = document.getLineNumber(element.getTextOffset());
70 | final int lineEndOffset = document.getLineEndOffset(lineNo);
71 | CommandProcessor.getInstance().executeCommand(project, new Runnable() {
72 | public void run() {
73 | document.insertString(lineEndOffset, " //eslint-disable-line");
74 | }
75 | }, null, null);
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
82 |
83 | public boolean isSuppressedFor(@NotNull PsiElement element) {
84 | // Property property = PsiTreeUtil.getParentOfType(element, Property.class, false);
85 | // JSFileImpl file;
86 | // if (property == null) {
87 | // PsiFile containingFile = element.getContainingFile();
88 | // if (containingFile instanceof JSFileImpl) {
89 | // file = (JSFileImpl) containingFile;
90 | // } else {
91 | // return false;
92 | // }
93 | // } else {
94 | // PsiElement prev = property.getPrevSibling();
95 | // while (prev instanceof PsiWhiteSpace || prev instanceof PsiComment) {
96 | // if (prev instanceof PsiComment) {
97 | // @NonNls String text = prev.getText();
98 | // if (text.contains("suppress") && text.contains('"' + getShortName() + '"')) return true;
99 | // }
100 | // prev = prev.getPrevSibling();
101 | // }
102 | // file = property.getPropertiesFile();
103 | // }
104 | // PsiElement leaf = file.getContainingFile().findElementAt(0);
105 | // while (leaf instanceof PsiWhiteSpace) leaf = leaf.getNextSibling();
106 | //
107 | // while (leaf instanceof PsiComment) {
108 | // @NonNls String text = leaf.getText();
109 | // if (text.contains("suppress") && text.contains('"' + getShortName() + '"') && text.contains("file")) {
110 | // return true;
111 | // }
112 | // leaf = leaf.getNextSibling();
113 | // if (leaf instanceof PsiWhiteSpace) leaf = leaf.getNextSibling();
114 | // // comment before first property get bound to the file, not property
115 | // if (leaf instanceof PropertiesList && leaf.getFirstChild() == property && text.contains("suppress") && text.contains("\"" + getShortName() + "\"")) {
116 | // return true;
117 | // }
118 | // }
119 |
120 | return false;
121 | }
122 |
123 | private static class SuppressForStatement extends SuppressIntentionAction {
124 | private final String rule;
125 |
126 | public SuppressForStatement(String rule) {
127 | this.rule = rule;
128 | }
129 |
130 | @NotNull
131 | public String getText() {
132 | return ESLintBundle.message("unused.property.suppress.for.statement");
133 | }
134 |
135 | @NotNull
136 | public String getFamilyName() {
137 | return ESLintBundle.message("unused.property.suppress.for.statement");
138 | }
139 |
140 | public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) {
141 | final JSElement property = PsiTreeUtil.getParentOfType(element, JSElement.class);
142 | return property != null && property.isValid();
143 | }
144 |
145 | public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException {
146 | final PsiFile file = element.getContainingFile();
147 | if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
148 |
149 | // InspectionManager inspectionManager = InspectionManager.getInstance(project);
150 | // ProblemDescriptor descriptor = inspectionManager.createProblemDescriptor(element, element, "", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
151 |
152 | final JSElement property = PsiTreeUtil.getParentOfType(element, JSElement.class);
153 | LOG.assertTrue(property != null);
154 | final int start = property.getTextRange().getStartOffset();
155 |
156 | @NonNls final Document doc = PsiDocumentManager.getInstance(project).getDocument(file);
157 | LOG.assertTrue(doc != null);
158 | final int line = doc.getLineNumber(start);
159 | final int lineEnd = doc.getLineEndOffset(line);
160 | doc.insertString(lineEnd, " //eslint-disable-line " + rule);
161 | }
162 | }
163 |
164 | private static class SuppressForFile extends SuppressIntentionAction {
165 | private final String rule;
166 |
167 | public SuppressForFile(String rule) {
168 | this.rule = rule;
169 | }
170 |
171 | @NotNull
172 | public String getText() {
173 | return ESLintBundle.message("unused.property.suppress.for.file");
174 | }
175 |
176 | @NotNull
177 | public String getFamilyName() {
178 | return ESLintBundle.message("unused.property.suppress.for.file");
179 | }
180 |
181 | public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) {
182 | return element.isValid() && element.getContainingFile() instanceof JSFile;
183 | }
184 |
185 | public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException {
186 | final PsiFile file = element.getContainingFile();
187 | if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
188 |
189 | @NonNls final Document doc = PsiDocumentManager.getInstance(project).getDocument(file);
190 | LOG.assertTrue(doc != null, file);
191 |
192 | // doc.insertString(0, "// eslint suppress inspection \"" + rule + "\" for whole file\n");
193 | doc.insertString(0, "/* eslint-disable */\n");
194 | }
195 | }
196 |
197 | //doc.insertString(lineStart, "/*eslint " + rule + ":0*/\n");
198 | }
--------------------------------------------------------------------------------
/src/com/eslint/settings/ESLintSettingsPage.form:
--------------------------------------------------------------------------------
1 |
2 |
226 |
--------------------------------------------------------------------------------
/src/com/eslint/settings/ESLintSettingsPage.java:
--------------------------------------------------------------------------------
1 | package com.eslint.settings;
2 |
3 | import com.eslint.ESLintProjectComponent;
4 | import com.eslint.utils.ESLintFinder;
5 | import com.eslint.utils.ESLintRunner;
6 | import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
7 | import com.intellij.execution.ExecutionException;
8 | import com.intellij.openapi.application.ApplicationManager;
9 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
10 | import com.intellij.openapi.options.Configurable;
11 | import com.intellij.openapi.project.Project;
12 | import com.intellij.openapi.vfs.VirtualFile;
13 | import com.intellij.psi.PsiManager;
14 | import com.intellij.ui.DocumentAdapter;
15 | import com.intellij.ui.HyperlinkLabel;
16 | import com.intellij.ui.TextFieldWithHistory;
17 | import com.intellij.ui.TextFieldWithHistoryWithBrowseButton;
18 | import com.intellij.util.NotNullProducer;
19 | import com.intellij.util.ui.UIUtil;
20 | import com.intellij.webcore.ui.SwingHelper;
21 | import com.wix.nodejs.NodeDetectionUtil;
22 | import com.wix.settings.ValidationUtils;
23 | import com.wix.settings.Validator;
24 | import com.wix.ui.PackagesNotificationPanel;
25 | import com.wix.utils.FileUtils;
26 | import org.apache.commons.lang.StringUtils;
27 | import org.jetbrains.annotations.Nls;
28 | import org.jetbrains.annotations.NotNull;
29 | import org.jetbrains.annotations.Nullable;
30 |
31 | import javax.swing.*;
32 | import javax.swing.event.DocumentEvent;
33 | import javax.swing.text.JTextComponent;
34 | import java.awt.*;
35 | import java.awt.event.ItemEvent;
36 | import java.awt.event.ItemListener;
37 | import java.io.File;
38 | import java.util.List;
39 | import java.util.Objects;
40 |
41 | //import com.intellij.javascript.nodejs.NodeDetectionUtil;
42 |
43 | //public class ESLintSettingsPage extends SearchableConfigurable.Parent.Abstract implements Configurable.NoScroll {
44 | public class ESLintSettingsPage implements Configurable {
45 | private static final String FIX_IT = "Fix it";
46 | private static final String HOW_TO_USE_ESLINT = "How to Use ESLint";
47 | private static final String HOW_TO_USE_LINK = "https://github.com/idok/eslint-plugin";
48 | protected Project project;
49 |
50 | private JCheckBox pluginEnabledCheckbox;
51 | private JTextField customRulesPathField;
52 | private JPanel panel;
53 | private JPanel errorPanel;
54 | private TextFieldWithHistoryWithBrowseButton eslintBinField2;
55 | private TextFieldWithHistoryWithBrowseButton nodeInterpreterField;
56 | private TextFieldWithHistoryWithBrowseButton eslintrcFile;
57 | private JRadioButton searchForEslintrcInRadioButton;
58 | private JRadioButton useProjectEslintrcRadioButton;
59 | private HyperlinkLabel usageLink;
60 | private JLabel ESLintConfigFilePathLabel;
61 | private JLabel rulesDirectoryLabel;
62 | private JLabel pathToEslintBinLabel;
63 | private JLabel nodeInterpreterLabel;
64 | private JCheckBox treatAllEslintIssuesCheckBox;
65 | private JLabel versionLabel;
66 | private TextFieldWithHistoryWithBrowseButton rulesPathField;
67 | private JLabel rulesDirectoryLabel1;
68 | private JTextField textFieldExt;
69 | private JLabel extensionsLabel;
70 | private JCheckBox autoFixCheckbox;
71 | private JCheckBox reportUnusedCheckbox;
72 | private final PackagesNotificationPanel packagesNotificationPanel;
73 |
74 | public ESLintSettingsPage(@NotNull final Project project) {
75 | this.project = project;
76 | configESLintBinField();
77 | configESLintRcField();
78 | configESLintRulesField();
79 | configNodeField();
80 | // searchForEslintrcInRadioButton.addItemListener(new ItemListener() {
81 | // public void itemStateChanged(ItemEvent e) {
82 | // eslintrcFile.setEnabled(e.getStateChange() == ItemEvent.DESELECTED);
83 | // System.out.println("searchForEslintrcInRadioButton: " + (e.getStateChange() == ItemEvent.SELECTED ? "checked" : "unchecked"));
84 | // }
85 | // });
86 | useProjectEslintrcRadioButton.addItemListener(new ItemListener() {
87 | public void itemStateChanged(ItemEvent e) {
88 | eslintrcFile.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
89 | // System.out.println("useProjectEslintrcRadioButton: " + (e.getStateChange() == ItemEvent.SELECTED ? "checked" : "unchecked"));
90 | }
91 | });
92 | pluginEnabledCheckbox.addItemListener(new ItemListener() {
93 | public void itemStateChanged(ItemEvent e) {
94 | boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
95 | setEnabledState(enabled);
96 | }
97 | });
98 |
99 | this.packagesNotificationPanel = new PackagesNotificationPanel(project);
100 | // GridConstraints gridConstraints = new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
101 | // GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
102 | // null, new Dimension(250, 150), null);
103 | errorPanel.add(this.packagesNotificationPanel.getComponent(), BorderLayout.CENTER);
104 |
105 | DocumentAdapter docAdp = new DocumentAdapter() {
106 | protected void textChanged(@NotNull DocumentEvent e) {
107 | updateLaterInEDT();
108 | }
109 | };
110 | eslintBinField2.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
111 | eslintrcFile.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
112 | nodeInterpreterField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
113 | rulesPathField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
114 | customRulesPathField.getDocument().addDocumentListener(docAdp);
115 | textFieldExt.getDocument().addDocumentListener(docAdp);
116 | }
117 |
118 | private File getProjectPath() {
119 | if (project.isDefault()) {
120 | return null;
121 | }
122 | return new File(Objects.requireNonNull(project.getBasePath()));
123 | }
124 |
125 | private void updateLaterInEDT() {
126 | UIUtil.invokeLaterIfNeeded(new Runnable() {
127 | public void run() {
128 | ESLintSettingsPage.this.update();
129 | }
130 | });
131 | }
132 |
133 | private void update() {
134 | ApplicationManager.getApplication().assertIsDispatchThread();
135 | validate();
136 | }
137 |
138 | private void setEnabledState(boolean enabled) {
139 | eslintrcFile.setEnabled(enabled);
140 | customRulesPathField.setEnabled(enabled);
141 | rulesPathField.setEnabled(enabled);
142 | searchForEslintrcInRadioButton.setEnabled(enabled);
143 | useProjectEslintrcRadioButton.setEnabled(enabled);
144 | eslintBinField2.setEnabled(enabled);
145 | reportUnusedCheckbox.setEnabled(enabled);
146 | autoFixCheckbox.setEnabled(enabled);
147 | nodeInterpreterField.setEnabled(enabled);
148 | ESLintConfigFilePathLabel.setEnabled(enabled);
149 | rulesDirectoryLabel.setEnabled(enabled);
150 | rulesDirectoryLabel1.setEnabled(enabled);
151 | pathToEslintBinLabel.setEnabled(enabled);
152 | nodeInterpreterLabel.setEnabled(enabled);
153 | treatAllEslintIssuesCheckBox.setEnabled(enabled);
154 | textFieldExt.setEnabled(enabled);
155 | extensionsLabel.setEnabled(enabled);
156 | }
157 |
158 | private void validateField(Validator validator, TextFieldWithHistoryWithBrowseButton field, boolean allowEmpty, String message) {
159 | if (!validatePath(field.getChildComponent().getText(), allowEmpty)) {
160 | validator.add(field.getChildComponent().getTextEditor(), message, FIX_IT);
161 | // addError(validator, field.getChildComponent().getTextEditor(), message, FIX_IT);
162 | }
163 | }
164 |
165 | private void validate() {
166 | if (!pluginEnabledCheckbox.isSelected()) {
167 | return;
168 | }
169 | Validator validator = new Validator();
170 | validateField(validator, eslintBinField2, false, "Path to eslint is invalid {{LINK}}");
171 | validateField(validator, eslintrcFile, true, "Path to eslintrc is invalid {{LINK}}"); //Please correct path to
172 | validateField(validator, nodeInterpreterField, false, "Path to node interpreter is invalid {{LINK}}");
173 | if (!validateDirectory(customRulesPathField.getText(), true)) {
174 | addError(validator, customRulesPathField, "Path to custom rules is invalid {{LINK}}", FIX_IT);
175 | }
176 | if (!validateDirectory(rulesPathField.getChildComponent().getText(), true)) {
177 | addError(validator, rulesPathField.getChildComponent().getTextEditor(), "Path to rules is invalid {{LINK}}", FIX_IT);
178 | }
179 | if (!validateExt(textFieldExt.getText())) {
180 | addError(validator, textFieldExt, "Extensions format is invalid, should be e.g. .js,.jsx without white space {{LINK}}", FIX_IT);
181 | }
182 | if (!validator.hasErrors() && !project.isDefault()) {
183 | getVersion();
184 | }
185 | packagesNotificationPanel.processErrors(validator);
186 | }
187 |
188 | private static void addError(Validator validator, @Nullable JTextComponent textComponent, @NotNull String errorHtmlDescriptionTemplate, @NotNull String linkText) {
189 | validator.add((JTextField) textComponent, errorHtmlDescriptionTemplate, linkText);
190 | // ValidationInfo error = new ValidationInfo(textComponent, errorHtmlDescriptionTemplate, linkText);
191 | // errors.add(error);
192 | }
193 |
194 | private static boolean validateExt(String ext) {
195 | return StringUtils.isEmpty(ext) || ext.matches("^(\\.\\w+,?)+$");
196 | }
197 |
198 | private ESLintRunner.ESLintSettings settings;
199 |
200 | private void getVersion() {
201 | if (settings != null &&
202 | areEqual(nodeInterpreterField, settings.node) &&
203 | areEqual(eslintBinField2, settings.eslintExecutablePath) &&
204 | settings.cwd.equals(project.getBasePath())
205 | ) {
206 | return;
207 | }
208 | settings = new ESLintRunner.ESLintSettings();
209 | settings.node = nodeInterpreterField.getChildComponent().getText();
210 | settings.eslintExecutablePath = eslintBinField2.getChildComponent().getText();
211 | settings.cwd = project.getBasePath();
212 | try {
213 | String version = ESLintRunner.runVersion(settings);
214 | versionLabel.setText(version.trim());
215 | } catch (ExecutionException e) {
216 | e.printStackTrace();
217 | }
218 | }
219 |
220 | private boolean validatePath(String path, boolean allowEmpty) {
221 | if (StringUtils.isEmpty(path)) {
222 | return allowEmpty;
223 | }
224 | File filePath = new File(path);
225 | if (filePath.isAbsolute()) {
226 | if (!filePath.exists() || !filePath.isFile()) {
227 | return false;
228 | }
229 | } else {
230 | if (project.isDefault()) {
231 | return false;
232 | }
233 | VirtualFile child = project.getBaseDir().findFileByRelativePath(path);
234 | if (child == null || !child.exists() || child.isDirectory()) {
235 | return false;
236 | }
237 | }
238 | return true;
239 | }
240 |
241 | private boolean validateDirectory(String path, boolean allowEmpty) {
242 | return ValidationUtils.validateDirectory(project, path, allowEmpty);
243 | }
244 |
245 | private static TextFieldWithHistory configWithDefaults(TextFieldWithHistoryWithBrowseButton field) {
246 | TextFieldWithHistory textFieldWithHistory = field.getChildComponent();
247 | textFieldWithHistory.setHistorySize(-1);
248 | textFieldWithHistory.setMinimumAndPreferredWidth(0);
249 | return textFieldWithHistory;
250 | }
251 |
252 | private void configESLintBinField() {
253 | configWithDefaults(eslintBinField2);
254 | SwingHelper.addHistoryOnExpansion(eslintBinField2.getChildComponent(), new NotNullProducer>() {
255 | @NotNull
256 | public List produce() {
257 | List newFiles = ESLintFinder.searchForESLintBin(getProjectPath());
258 | return FileUtils.toAbsolutePath(newFiles);
259 | }
260 | });
261 | SwingHelper.installFileCompletionAndBrowseDialog(project, eslintBinField2, "Select ESLint.js Cli", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
262 | }
263 |
264 | private void configESLintRulesField() {
265 | TextFieldWithHistory textFieldWithHistory = rulesPathField.getChildComponent();
266 | SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer>() {
267 | @NotNull
268 | public List produce() {
269 | return ESLintFinder.tryFindRulesAsString(getProjectPath());
270 | }
271 | });
272 | SwingHelper.installFileCompletionAndBrowseDialog(project, rulesPathField, "Select Built in Rules", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
273 | }
274 |
275 | private void configESLintRcField() {
276 | TextFieldWithHistory textFieldWithHistory = configWithDefaults(eslintrcFile);
277 | SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer>() {
278 | @NotNull
279 | public List produce() {
280 | return ESLintFinder.searchForESLintRCFiles(getProjectPath());
281 | }
282 | });
283 | SwingHelper.installFileCompletionAndBrowseDialog(project, eslintrcFile, "Select ESLint Config", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
284 | }
285 |
286 | private void configNodeField() {
287 | TextFieldWithHistory textFieldWithHistory = configWithDefaults(nodeInterpreterField);
288 | SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer>() {
289 | @NotNull
290 | public List produce() {
291 | List newFiles = NodeDetectionUtil.listAllPossibleNodeInterpreters();
292 | return FileUtils.toAbsolutePath(newFiles);
293 | }
294 | });
295 | SwingHelper.installFileCompletionAndBrowseDialog(project, nodeInterpreterField, "Select Node Interpreter", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
296 | }
297 |
298 | @Nls
299 | @Override
300 | public String getDisplayName() {
301 | return "ESLint";
302 | }
303 |
304 | @Nullable
305 | @Override
306 | public String getHelpTopic() {
307 | return null;
308 | }
309 |
310 | @Nullable
311 | @Override
312 | public JComponent createComponent() {
313 | loadSettings();
314 | return panel;
315 | }
316 |
317 | private static boolean areEqual(TextFieldWithHistoryWithBrowseButton field, String value) {
318 | return field.getChildComponent().getText().equals(value);
319 | }
320 |
321 | @Override
322 | public boolean isModified() {
323 | Settings s = getSettings();
324 | return pluginEnabledCheckbox.isSelected() != s.pluginEnabled ||
325 | autoFixCheckbox.isSelected() != s.autoFix ||
326 | reportUnusedCheckbox.isSelected() != s.reportUnused ||
327 | !areEqual(eslintBinField2, s.eslintExecutable) ||
328 | !areEqual(nodeInterpreterField, s.nodeInterpreter) ||
329 | treatAllEslintIssuesCheckBox.isSelected() != s.treatAllEslintIssuesAsWarnings ||
330 | !customRulesPathField.getText().equals(s.rulesPath) ||
331 | !textFieldExt.getText().equals(s.ext) ||
332 | !areEqual(rulesPathField, s.builtinRulesPath) ||
333 | !getESLintRCFile().equals(s.eslintRcFile);
334 | }
335 |
336 | private String getESLintRCFile() {
337 | return useProjectEslintrcRadioButton.isSelected() ? eslintrcFile.getChildComponent().getText() : "";
338 | }
339 |
340 | @Override
341 | public void apply() {
342 | saveSettings();
343 | PsiManager.getInstance(project).dropResolveCaches();
344 | }
345 |
346 | protected void saveSettings() {
347 | Settings settings = getSettings();
348 | settings.pluginEnabled = pluginEnabledCheckbox.isSelected();
349 | settings.autoFix = autoFixCheckbox.isSelected();
350 | settings.reportUnused = reportUnusedCheckbox.isSelected();
351 | settings.eslintExecutable = eslintBinField2.getChildComponent().getText();
352 | settings.nodeInterpreter = nodeInterpreterField.getChildComponent().getText();
353 | settings.eslintRcFile = getESLintRCFile();
354 | settings.rulesPath = customRulesPathField.getText();
355 | settings.builtinRulesPath = rulesPathField.getChildComponent().getText();
356 | settings.treatAllEslintIssuesAsWarnings = treatAllEslintIssuesCheckBox.isSelected();
357 | settings.ext = textFieldExt.getText();
358 | if (!project.isDefault()) {
359 | project.getComponent(ESLintProjectComponent.class).validateSettings();
360 | DaemonCodeAnalyzer.getInstance(project).restart();
361 | }
362 | validate();
363 | }
364 |
365 | protected void loadSettings() {
366 | Settings settings = getSettings();
367 | setEnabledState(settings.pluginEnabled);
368 | pluginEnabledCheckbox.setSelected(settings.pluginEnabled);
369 | autoFixCheckbox.setSelected(settings.autoFix);
370 | reportUnusedCheckbox.setSelected(settings.reportUnused);
371 | eslintBinField2.getChildComponent().setText(settings.eslintExecutable);
372 | eslintrcFile.getChildComponent().setText(settings.eslintRcFile);
373 | nodeInterpreterField.getChildComponent().setText(settings.nodeInterpreter);
374 | customRulesPathField.setText(settings.rulesPath);
375 | textFieldExt.setText(settings.ext);
376 | rulesPathField.getChildComponent().setText(settings.builtinRulesPath);
377 | useProjectEslintrcRadioButton.setSelected(StringUtils.isNotEmpty(settings.eslintRcFile));
378 | searchForEslintrcInRadioButton.setSelected(StringUtils.isEmpty(settings.eslintRcFile));
379 | eslintrcFile.setEnabled(useProjectEslintrcRadioButton.isSelected());
380 | treatAllEslintIssuesCheckBox.setSelected(settings.treatAllEslintIssuesAsWarnings);
381 | }
382 |
383 | @Override
384 | public void reset() {
385 | loadSettings();
386 | }
387 |
388 | @Override
389 | public void disposeUIResources() {
390 | }
391 |
392 | // @Override
393 | // protected Configurable[] buildConfigurables() {
394 | // return new Configurable[0];
395 | // }
396 |
397 | protected Settings getSettings() {
398 | return Settings.getInstance(project);
399 | }
400 |
401 | private void createUIComponents() {
402 | // TODO: place custom component creation code here
403 | usageLink = SwingHelper.createWebHyperlink(HOW_TO_USE_ESLINT, HOW_TO_USE_LINK);
404 | }
405 |
406 | // @NotNull
407 | // @Override
408 | // public String getId() {
409 | // return "com.eslint.elintconfig";
410 | // }
411 | }
412 |
--------------------------------------------------------------------------------
/src/com/eslint/settings/Settings.java:
--------------------------------------------------------------------------------
1 | package com.eslint.settings;
2 |
3 | import com.eslint.utils.ESLintFinder;
4 | import com.intellij.openapi.components.*;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.util.xmlb.XmlSerializerUtil;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | @State(
11 | name = "ESLintProjectComponent",
12 | storages = {@Storage("eslintPlugin.xml")}
13 | )
14 | public class Settings implements PersistentStateComponent {
15 | public String eslintRcFile = ESLintFinder.ESLINTRC;
16 | public String rulesPath = "";
17 | public String builtinRulesPath = "";
18 | public String eslintExecutable = "";
19 | public String nodeInterpreter;
20 | public boolean treatAllEslintIssuesAsWarnings;
21 | public boolean pluginEnabled;
22 | public boolean autoFix;
23 | public boolean reportUnused;
24 | public String ext = "";
25 |
26 | protected Project project;
27 |
28 | public static Settings getInstance(Project project) {
29 | Settings settings = ServiceManager.getService(project, Settings.class);
30 | settings.project = project;
31 | return settings;
32 | }
33 |
34 | @Nullable
35 | @Override
36 | public Settings getState() {
37 | return this;
38 | }
39 |
40 | @Override
41 | public void loadState(@NotNull Settings state) {
42 | XmlSerializerUtil.copyBean(state, this);
43 | }
44 |
45 | public String getVersion() {
46 | return nodeInterpreter + eslintExecutable + eslintRcFile + rulesPath + builtinRulesPath + ext + autoFix + reportUnused;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/eslint/utils/CliBuilder.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.wix.nodejs.CLI;
6 | import com.wix.nodejs.NodeRunner;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | final class CliBuilder {
10 |
11 | public static final String V = "-v";
12 | public static final String RULESDIR = "--rulesdir";
13 | public static final String EXT = "--ext";
14 | public static final String C = "-c";
15 | public static final String FIX = "--fix";
16 | public static final String FORMAT = "--format";
17 | public static final String REPORT_UNUSED = "--report-unused-disable-directives";
18 | public static final String JSON = "json";
19 |
20 | private CliBuilder() {
21 | }
22 |
23 | @NotNull
24 | static GeneralCommandLine create(@NotNull ESLintRunner.ESLintSettings settings) {
25 | return NodeRunner.createCommandLine(settings.cwd, settings.node, settings.eslintExecutablePath);
26 | }
27 |
28 | @NotNull
29 | static GeneralCommandLine createLint(@NotNull ESLintRunner.ESLintSettings settings) {
30 | GeneralCommandLine commandLine = create(settings);
31 | // TODO validate arguments (file exist etc)
32 | commandLine.addParameter(settings.targetFile);
33 | CLI.addParamIfNotEmpty(commandLine, C, settings.config);
34 | if (StringUtil.isNotEmpty(settings.rules)) {
35 | CLI.addParam(commandLine, RULESDIR, "['" + settings.rules + "']");
36 | }
37 | if (StringUtil.isNotEmpty(settings.ext)) {
38 | CLI.addParam(commandLine, EXT, settings.ext);
39 | }
40 | if (settings.fix) {
41 | commandLine.addParameter(FIX);
42 | }
43 | if (settings.reportUnused) {
44 | commandLine.addParameter(REPORT_UNUSED);
45 | }
46 | CLI.addParam(commandLine, FORMAT, JSON);
47 | return commandLine;
48 | }
49 |
50 | @NotNull
51 | static GeneralCommandLine createFix(@NotNull ESLintRunner.ESLintSettings settings) {
52 | GeneralCommandLine commandLine = createLint(settings);
53 | commandLine.addParameter(FIX);
54 | return commandLine;
55 | }
56 |
57 | @NotNull
58 | static GeneralCommandLine createVersion(@NotNull ESLintRunner.ESLintSettings settings) {
59 | GeneralCommandLine commandLine = create(settings);
60 | commandLine.addParameter(V);
61 | return commandLine;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/com/eslint/utils/ESLintFinder.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import com.intellij.openapi.util.Condition;
4 | import com.intellij.openapi.util.SystemInfo;
5 | import com.intellij.openapi.util.io.FileUtil;
6 | import com.intellij.util.Function;
7 | import com.intellij.util.containers.ContainerUtil;
8 | import com.wix.nodejs.NodeFinder;
9 | import com.wix.utils.FileUtils;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.io.File;
13 | import java.io.FilenameFilter;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | public final class ESLintFinder {
18 | public static final String ESLINTRC = ".eslintrc";
19 | public static final String ESLINT_BASE_NAME = NodeFinder.getBinName("eslint");
20 |
21 | // TODO figure out a way to automatically get this path or add it to config
22 | // should read from /usr/local/lib/node_modules/eslint/lib/rules
23 | // public static String defaultPath = "/usr/local/lib/node_modules/eslint/lib/rules";
24 | // c:/users/user/appdata/roaming/npm/node_modules
25 |
26 | private ESLintFinder() {
27 | }
28 |
29 | public static List tryFindRules(File projectRoot) {
30 | List options = new ArrayList();
31 | String relativeRules = NodeFinder.buildPath(NodeFinder.NODE_MODULES, "eslint", "lib", "rules");
32 | File local = new File(projectRoot, relativeRules);
33 | options.add(local);
34 | if (SystemInfo.isWindows) {
35 | File file = new File("C:\\Program Files (x86)\\nodejs", relativeRules);
36 | options.add(file);
37 | file = new File("C:\\Program Files\\nodejs", relativeRules);
38 | options.add(file);
39 | } else {
40 | File file = new File("/usr/local/lib", relativeRules);
41 | options.add(file);
42 | }
43 | List valid = ContainerUtil.filter(options, new Condition() {
44 | @Override
45 | public boolean value(File file) {
46 | return file.exists() && file.isDirectory();
47 | }
48 | });
49 | return valid;
50 | }
51 |
52 | public static List tryFindRulesAsString(final File projectRoot) {
53 | List files = tryFindRules(projectRoot);
54 | return ContainerUtil.map(files, new Function() {
55 | public String fun(File file) {
56 | // return FileUtils.makeRelative(projectRoot, file);
57 | if (projectRoot != null && FileUtil.isAncestor(projectRoot, file, true)) {
58 | return FileUtils.makeRelative(projectRoot, file);
59 | }
60 | return file.toString();
61 | }
62 | });
63 | }
64 |
65 | @NotNull
66 | public static List listPossibleESLintExe() {
67 | return NodeFinder.searchNodeModulesBin(ESLINT_BASE_NAME);
68 | }
69 |
70 | @NotNull
71 | public static List searchForESLintBin(File projectRoot) {
72 | return NodeFinder.searchAllScopesForBin(projectRoot, ESLINT_BASE_NAME);
73 | }
74 |
75 | /**
76 | * find possible eslint rc files
77 | *
78 | * @param projectRoot project root
79 | * @return list
80 | */
81 | public static List searchForESLintRCFiles(final File projectRoot) {
82 | FilenameFilter filter = new FilenameFilter() {
83 | @Override
84 | public boolean accept(File file, String name) {
85 | return name.equals(ESLINTRC) || name.startsWith(ESLINTRC + '.');
86 | }
87 | };
88 | // return Arrays.asList(files);
89 | List files = FileUtils.recursiveVisitor(projectRoot, filter);
90 | return ContainerUtil.map(files, new Function() {
91 | public String fun(String curFile) {
92 | return FileUtils.makeRelative(projectRoot, new File(curFile));
93 | }
94 | });
95 | }
96 | }
--------------------------------------------------------------------------------
/src/com/eslint/utils/ESLintRunner.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import com.eslint.ESLintProjectComponent;
4 | import com.intellij.execution.ExecutionException;
5 | import com.intellij.execution.configurations.GeneralCommandLine;
6 | import com.intellij.execution.process.ProcessOutput;
7 | import com.intellij.notification.NotificationType;
8 | import com.intellij.openapi.diagnostic.Logger;
9 | import com.wix.nodejs.NodeRunner;
10 | import org.jetbrains.annotations.NotNull;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import java.io.File;
14 | import java.util.concurrent.TimeUnit;
15 |
16 | public final class ESLintRunner {
17 | private ESLintRunner() {
18 | }
19 |
20 | private static final Logger LOG = Logger.getInstance(ESLintRunner.class);
21 |
22 | private static final int TIME_OUT = (int) TimeUnit.SECONDS.toMillis(120L);
23 |
24 | public static class ESLintSettings {
25 | public String node;
26 | public String eslintExecutablePath;
27 | public String rules;
28 | public String config;
29 | public String cwd;
30 | public String targetFile;
31 | public String ext;
32 | public boolean fix;
33 | public boolean reportUnused;
34 | }
35 |
36 | public static ESLintSettings buildSettings(@NotNull String cwd, @NotNull String path, @NotNull ESLintProjectComponent component) {
37 | return buildSettings(cwd, path, component.nodeInterpreter, component.eslintExecutable, component.eslintRcFile, component.customRulesPath, component.ext, component.autoFix, component.reportUnused);
38 | }
39 |
40 | private static ESLintSettings buildSettings(@NotNull String cwd, @NotNull String path, @NotNull String nodeInterpreter, @NotNull String eslintBin, @Nullable String eslintrc,
41 | @Nullable String rulesdir, @Nullable String ext, boolean autoFix, boolean reportUnused) {
42 | ESLintRunner.ESLintSettings settings = new ESLintRunner.ESLintSettings();
43 | settings.cwd = cwd;
44 | settings.eslintExecutablePath = eslintBin;
45 | settings.node = nodeInterpreter;
46 | settings.rules = rulesdir;
47 | settings.config = eslintrc;
48 | settings.targetFile = path;
49 | settings.ext = ext;
50 | settings.fix = autoFix;
51 | settings.reportUnused = reportUnused;
52 | return settings;
53 | }
54 |
55 | @NotNull
56 | public static ProcessOutput lint(@NotNull ESLintSettings settings) throws ExecutionException {
57 | GeneralCommandLine commandLine = CliBuilder.createLint(settings);
58 | return NodeRunner.execute(commandLine, TIME_OUT);
59 | }
60 |
61 | @NotNull
62 | public static Result lint(@NotNull String cwd, @NotNull String path, @NotNull ESLintProjectComponent component) {
63 | ESLintRunner.ESLintSettings settings = ESLintRunner.buildSettings(cwd, path, component);
64 | try {
65 | ProcessOutput output = ESLintRunner.lint(settings);
66 | return Result.processResults(output);
67 | } catch (ExecutionException e) {
68 | LOG.warn("Could not lint file", e);
69 | ESLintProjectComponent.showNotification("Error running ESLint inspection: " + e.getMessage() + "\ncwd: " + cwd + "\ncommand: " + component.eslintExecutable, NotificationType.WARNING);
70 | e.printStackTrace();
71 | return Result.createError(e.getMessage());
72 | }
73 | }
74 |
75 | // @NotNull
76 | // public static Result lint(@NotNull String cwd, @NotNull String path, @NotNull String nodeInterpreter, @NotNull String eslintBin, @Nullable String eslintrc, @Nullable String rulesdir, @Nullable String ext, boolean autoFix) {
77 | // ESLintRunner.ESLintSettings settings = ESLintRunner.buildSettings(cwd, path, nodeInterpreter, eslintBin, eslintrc, rulesdir, ext, autoFix);
78 | // try {
79 | // ProcessOutput output = ESLintRunner.lint(settings);
80 | // return Result.processResults(output);
81 | // } catch (ExecutionException e) {
82 | // LOG.warn("Could not lint file", e);
83 | // ESLintProjectComponent.showNotification("Error running ESLint inspection: " + e.getMessage() + "\ncwd: " + cwd + "\ncommand: " + eslintBin, NotificationType.WARNING);
84 | // e.printStackTrace();
85 | // return Result.createError(e.getMessage());
86 | // }
87 | // }
88 |
89 | @NotNull
90 | public static ProcessOutput fix(@NotNull ESLintSettings settings) throws ExecutionException {
91 | GeneralCommandLine commandLine = CliBuilder.createFix(settings);
92 | return NodeRunner.execute(commandLine, TIME_OUT);
93 | }
94 |
95 | @NotNull
96 | private static ProcessOutput version(@NotNull ESLintSettings settings) throws ExecutionException {
97 | GeneralCommandLine commandLine = CliBuilder.createVersion(settings);
98 | return NodeRunner.execute(commandLine, TIME_OUT);
99 | }
100 |
101 | @NotNull
102 | public static String runVersion(@NotNull ESLintSettings settings) throws ExecutionException {
103 | if (!new File(settings.eslintExecutablePath).exists()) {
104 | LOG.warn("Calling version with invalid eslint exe " + settings.eslintExecutablePath);
105 | return "";
106 | }
107 | ProcessOutput out = version(settings);
108 | if (out.getExitCode() == 0) {
109 | return out.getStdout().trim();
110 | }
111 | return "";
112 | }
113 | }
--------------------------------------------------------------------------------
/src/com/eslint/utils/FileResult.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import java.util.List;
4 |
5 | public class FileResult {
6 | public String filePath;
7 | public List messages;
8 | public int errorCount;
9 | public int warningCount;
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/eslint/utils/JSBinaryExpressionUtil.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.intellij.lang.javascript.JSTokenTypes;
5 | import com.intellij.lang.javascript.psi.JSBinaryExpression;
6 | import com.intellij.openapi.util.Condition;
7 | import com.intellij.psi.PsiElement;
8 | import com.intellij.psi.tree.TokenSet;
9 | import com.intellij.psi.util.PsiTreeUtil;
10 |
11 | public final class JSBinaryExpressionUtil {
12 | private static final TokenSet BINARY_OPERATIONS = TokenSet.orSet(JSTokenTypes.OPERATIONS, JSTokenTypes.RELATIONAL_OPERATIONS);
13 |
14 | private JSBinaryExpressionUtil() {}
15 |
16 | public static ASTNode getOperator(PsiElement element) {
17 | PsiElement binary = PsiTreeUtil.findFirstParent(element, new Condition() {
18 | @Override
19 | public boolean value(PsiElement psiElement) {
20 | return psiElement instanceof JSBinaryExpression;
21 | }
22 | });
23 | return binary == null ? null : binary.getNode().getChildren(BINARY_OPERATIONS)[0];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/com/eslint/utils/Result.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.reflect.TypeToken;
6 | import com.intellij.execution.process.ProcessOutput;
7 |
8 | import java.lang.reflect.Type;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * ESLint result
14 | * Created by idok on 8/25/14.
15 | */
16 | public class Result {
17 | public List warns = new ArrayList();
18 | public String errorOutput;
19 |
20 | private static List parseInternal(String json) {
21 | GsonBuilder builder = new GsonBuilder();
22 | // builder.registerTypeAdapterFactory(adapter);
23 | Gson g = builder.setPrettyPrinting().create();
24 | Type listType = new TypeToken>() {}.getType();
25 | return g.fromJson(json, listType);
26 | }
27 |
28 | public static Result processResults(ProcessOutput output) {
29 | Result result = new Result();
30 | result.errorOutput = output.getStderr();
31 | try {
32 | List fileResults = parseInternal(output.getStdout());
33 | if (fileResults != null && !fileResults.isEmpty()) {
34 | result.warns = fileResults.get(0).messages;
35 | }
36 | } catch (Exception e) {
37 | result.errorOutput = output.getStdout();
38 | // result.errorOutput = e.toString();
39 | }
40 | return result;
41 | }
42 |
43 | public static Result createError(String error) {
44 | Result result = new Result();
45 | result.errorOutput = error;
46 | return result;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/eslint/utils/VerifyMessage.java:
--------------------------------------------------------------------------------
1 | package com.eslint.utils;
2 |
3 | public class VerifyMessage {
4 | public int severity;
5 | public String message;
6 | public String ruleId;
7 | public int line;
8 | public int column;
9 | }
--------------------------------------------------------------------------------
/src/icons/ESLintIcons.java:
--------------------------------------------------------------------------------
1 | package icons;
2 |
3 | import com.intellij.openapi.util.IconLoader;
4 |
5 | import javax.swing.Icon;
6 |
7 | public final class ESLintIcons {
8 | private ESLintIcons() {
9 | }
10 |
11 | public static final Icon ESLint = IconLoader.getIcon("/icons/fileTypes/eslint16x16.png", ESLintIcons.class);
12 | }
13 |
--------------------------------------------------------------------------------
/src/icons/fileTypes/eslint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idok/eslint-plugin/08cead4d3a9da827e77513ea8300e6af881bef1b/src/icons/fileTypes/eslint.png
--------------------------------------------------------------------------------
/src/icons/fileTypes/eslint16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idok/eslint-plugin/08cead4d3a9da827e77513ea8300e6af881bef1b/src/icons/fileTypes/eslint16x16.png
--------------------------------------------------------------------------------
/testData/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-array-constructor": 2,
4 | "no-catch-shadow": 2,
5 | "no-comma-dangle": 2,
6 | "no-cond-assign": 2,
7 | "no-constant-condition": 2,
8 | "no-control-regex": 2,
9 | "no-div-regex": 0,
10 | "no-else-return": 1,
11 | "no-empty-class": 2,
12 | "no-empty-label": 2,
13 | "no-eq-null": 1,
14 | "no-extend-native": 2,
15 | "no-extra-boolean-cast": 2,
16 | "no-extra-strict": 2,
17 | "no-global-strict": 2,
18 | "no-inner-declarations": [2, "functions"],
19 | "no-iterator": 2,
20 | "no-labels": 2,
21 | "no-lone-blocks": 2,
22 | "no-lonely-if": 1,
23 | "no-loop-func": 2,
24 | "no-mixed-requires": [0, false],
25 | "no-negated-in-lhs": 2,
26 | "no-nested-ternary": 0,
27 | "no-new-require": 0,
28 | "no-octal-escape": 2,
29 | "no-path-concat": 0,
30 | "no-process-exit": 2,
31 | "no-proto": 2,
32 | "no-redeclare": 2,
33 | "no-regex-spaces": 2,
34 | "no-restricted-modules": 0,
35 | "no-script-url": 2,
36 | "no-sequences": 2,
37 | "no-shadow": 2,
38 | "no-shadow-restricted-names": 2,
39 | "no-spaced-func": 2,
40 | "no-space-before-semi": 2,
41 | "no-sparse-arrays": 2,
42 | "no-sync": 0,
43 | "no-undef": 2,
44 | "no-unused-expressions": 2,
45 | "no-unused-vars": [0, {"vars": "local", "args": "after-used"}],
46 | "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
47 | "no-wrap-func": 2,
48 | "yoda": 2,
49 |
50 | "block-scoped-var": 0,
51 | "brace-style": [0, "1tbs"],
52 | "consistent-return": 2,
53 | "consistent-this": [0, "that"],
54 | "curly": [2, "all"],
55 | "default-case": 0,
56 | "func-names": 0,
57 | "func-style": [0, "declaration"],
58 | "max-depth": [0, 4],
59 | "max-len": [0, 80, 4],
60 | "max-nested-callbacks": [0, 2],
61 | "handle-callback-err": 0,
62 | "one-var": 0,
63 | "sort-vars": 0,
64 | "space-after-keywords": [0, "always"],
65 | "space-in-brackets": [0, "never"],
66 | "space-infix-ops": 2,
67 | "space-return-throw-case": 2,
68 | "space-unary-word-ops": 0,
69 | "strict": 2,
70 | "valid-typeof": 2,
71 | "wrap-regex": 0,
72 |
73 | "no-alert": 0,
74 | "no-caller": 2,
75 | "no-bitwise": 0,
76 | "no-console": 0,
77 | "no-underscore-dangle": 0,
78 | "no-debugger": 0,
79 | "no-dupe-keys": 1,
80 | "no-empty": 2,
81 | "no-eval": 1,
82 | "no-ex-assign": 2,
83 | "no-extra-parens": 0,
84 | "no-extra-semi": 1,
85 | "no-floating-decimal": 0,
86 | "no-func-assign": 1,
87 | "no-invalid-regexp": 1,
88 | "no-implied-eval": 2,
89 | "no-with": 2,
90 | "no-fallthrough": 2,
91 | "no-unreachable": 2,
92 | "no-undef-init": 2,
93 | "no-octal": 2,
94 | "no-obj-calls": 2,
95 | "no-new-wrappers": 2,
96 | "no-new": 2,
97 | "no-new-func": 2,
98 | "no-native-reassign": 2,
99 | "no-plusplus": 0,
100 | "no-delete-var": 2,
101 | "no-return-assign": 2,
102 | "no-new-object": 2,
103 | "no-label-var": 2,
104 | "no-ternary": 0,
105 | "no-self-compare": 1,
106 | "no-use-before-define": 0,
107 | "valid-jsdoc": 0,
108 | "eol-last": 0,
109 |
110 | "camelcase": 0,
111 | "dot-notation": 1,
112 | "eqeqeq": 1,
113 | "new-parens": 2,
114 | "guard-for-in": 0,
115 | "radix": 0,
116 | "new-cap": 2,
117 | "quote-props": 0,
118 | "semi": 2,
119 | "use-isnan": 2,
120 | "quotes": [0, "single"],
121 | "max-params": [0, 3],
122 | "max-statements": [0, 10],
123 | "complexity": [0, 11],
124 | "wrap-iife": 1,
125 | "no-multi-str": 2,
126 |
127 | "enforce-package-access": 0,
128 | "module-definition": 0
129 | },
130 | "env": {
131 | "browser": true,
132 | "node": true,
133 | "amd": true
134 | },
135 | "globals": {
136 | "_": false,
137 | "$": false,
138 | "requirejs": true,
139 | "xdescribe": false,
140 | "describe": false,
141 | "ddescribe": false,
142 | "iit": false,
143 | "it": false,
144 | "xit": false,
145 | "beforeEach": false,
146 | "afterEach": false,
147 | "jasmine": false,
148 | "expect": false,
149 | "waitsFor": false,
150 | "waits": false,
151 | "runs": false,
152 | "any": false,
153 | "spyOn": false,
154 | "createSpy": false,
155 | "DocumentTouch": false
156 | }
157 | }
--------------------------------------------------------------------------------
/testData/inspections/eqeqeq.js:
--------------------------------------------------------------------------------
1 | function f() {
2 | 'use strict';
3 | var e;
4 | if (e == 3) {
5 | return;
6 | }
7 | if (e != 3) {
8 | return;
9 | }
10 | }
--------------------------------------------------------------------------------
/testData/inspections/no-array-constructor.js:
--------------------------------------------------------------------------------
1 | function f() {
2 | 'use strict';
3 | var e = new Array();
4 | }
--------------------------------------------------------------------------------
/testData/inspections/no-lonely-if.js:
--------------------------------------------------------------------------------
1 | var a;
2 | if (a) {
3 |
4 | } else {
5 | if (a) {
6 |
7 | }
8 | }
--------------------------------------------------------------------------------
/testData/inspections/no-negated-in-lhs.js:
--------------------------------------------------------------------------------
1 | var a;
2 | var b;
3 |
4 | if (! a in b) {
5 | a = 3;
6 | }
7 |
8 | var x = ! a in b;
--------------------------------------------------------------------------------
/testData/inspections/no-new-object.js:
--------------------------------------------------------------------------------
1 | function f() {
2 | 'use strict';
3 | var e = new Object();
4 | }
--------------------------------------------------------------------------------
/testData/inspections/valid-typeof.js:
--------------------------------------------------------------------------------
1 | var a;
2 |
3 | if (typeof a === 'banana' ) {
4 | a = 3;
5 | }
--------------------------------------------------------------------------------
/tests/com/eslint/ESLintRunnerTest.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
2 |
3 | public class ESLintRunnerTest {
4 | public static final String ESLINT_BIN = "";
5 | public static final String NODE_INTERPRETER = "";
6 | }
7 |
--------------------------------------------------------------------------------
/tests/com/eslint/ESLintTest.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
2 |
3 | import com.eslint.settings.Settings;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.testFramework.fixtures.*;
6 |
7 | public class ESLintTest extends LightPlatformCodeInsightFixtureTestCase {
8 | @Override
9 | protected String getTestDataPath() {
10 | return TestUtils.getTestDataPath();
11 | }
12 |
13 | @Override
14 | protected void setUp() throws Exception {
15 | super.setUp();
16 | }
17 |
18 | @Override
19 | protected boolean isWriteActionRequired() {
20 | return false;
21 | }
22 |
23 | protected void doTest(final String file) {
24 | Project project = myFixture.getProject();
25 | Settings settings = Settings.getInstance(project);
26 | settings.eslintExecutable = ESLintRunnerTest.ESLINT_BIN;
27 | settings.eslintRcFile = getTestDataPath() + "/.eslintrc";
28 | settings.nodeInterpreter = ESLintRunnerTest.NODE_INTERPRETER;
29 | settings.rulesPath = "";
30 | settings.pluginEnabled = true;
31 | myFixture.configureByFile(file);
32 | myFixture.enableInspections(new ESLintInspection());
33 | myFixture.checkHighlighting(true, false, true);
34 | }
35 |
36 | protected void doTest() {
37 | String name = getTestName(true).replaceAll("_", "-");
38 | doTest("/inspections/" + name + ".js");
39 | }
40 |
41 | public void testEqeqeq() {
42 | doTest();
43 | }
44 |
45 | public void testNo_negated_in_lhs() {
46 | doTest();
47 | }
48 |
49 | public void testValid_typeof() {
50 | doTest();
51 | }
52 |
53 | public void testNo_lonely_if() {
54 | doTest();
55 | }
56 |
57 | public void testNo_new_object() {
58 | doTest();
59 | }
60 |
61 | public void testNo_array_constructor() {
62 | doTest();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/com/eslint/TestUtils.java:
--------------------------------------------------------------------------------
1 | package com.eslint;
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 | 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 | 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 |
--------------------------------------------------------------------------------