-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e4251ce
Showing
21 changed files
with
1,129 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.gradle/ | ||
.idea/ | ||
build/ | ||
|
||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2017 Piotr Wielgolaski | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
the Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Intellij plugin for validating AWS CloudFormation templates using cfn-lint | ||
|
||
|
||
### Install plugin | ||
|
||
Download JAR file from [releases](https://github.com/binxio/cfn-lint-plugin/releases) section. Then follow JetBrains [Installing Plugin from Disk](https://www.jetbrains.com/help/idea/managing-plugins.html#installing-plugins-from-disk) instructions. | ||
|
||
|
||
## Credits | ||
Inspired by [shellcheck-plugin](https://github.com/pwielgolaski/shellcheck) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.jetbrains.intellij' version '0.3.5' | ||
id 'io.freefair.git-version' version '2.5.9' | ||
} | ||
|
||
group 'io.binx.cfnlint.plugin' | ||
|
||
sourceCompatibility = 1.8 | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
testCompile group: 'junit', name: 'junit', version: '4.12' | ||
} | ||
|
||
intellij { | ||
version '2018.1.4' | ||
} | ||
patchPluginXml { | ||
changeNotes """ | ||
first release. work in progress. | ||
""" | ||
} | ||
|
||
apply plugin: "io.freefair.git-version" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
rootProject.name = 'cfn-lint-plugin' | ||
|
27 changes: 27 additions & 0 deletions
27
src/main/java/io/binx/cfnlint/plugin/AnnotationResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package io.binx.cfnlint.plugin; | ||
|
||
import io.binx.cfnlint.plugin.utils.CheckResult; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
class AnnotationResult { | ||
|
||
private final CheckAnnotationInput input; | ||
private final CheckResult result; | ||
|
||
AnnotationResult(CheckAnnotationInput input, CheckResult result) { | ||
this.input = input; | ||
this.result = result; | ||
} | ||
|
||
public List<CheckResult.Issue> getIssues() { | ||
return Optional.ofNullable(result).map(CheckResult::getIssues).orElse(Collections.emptyList()); | ||
} | ||
|
||
public CheckAnnotationInput getInput() { | ||
return input; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package io.binx.cfnlint.plugin; | ||
|
||
import com.intellij.CommonBundle; | ||
import org.jetbrains.annotations.NonNls; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.PropertyKey; | ||
|
||
import java.lang.ref.Reference; | ||
import java.lang.ref.SoftReference; | ||
import java.util.ResourceBundle; | ||
|
||
public final class Bundle { | ||
|
||
|
||
|
||
public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) { | ||
return CommonBundle.message(getBundle(), key, params); | ||
} | ||
|
||
@NonNls | ||
private static final String BUNDLE = "io.binx.cfnlint.plugin.Bundle"; | ||
private static Reference<ResourceBundle> ourBundle; | ||
private Bundle() { | ||
} | ||
|
||
private static ResourceBundle getBundle() { | ||
ResourceBundle bundle = com.intellij.reference.SoftReference.dereference(ourBundle); | ||
if (bundle == null) { | ||
bundle = ResourceBundle.getBundle(BUNDLE); | ||
ourBundle = new SoftReference<>(bundle); | ||
} | ||
return bundle; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/io/binx/cfnlint/plugin/CheckAnnotationInput.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package io.binx.cfnlint.plugin; | ||
|
||
import com.intellij.psi.PsiFile; | ||
|
||
class CheckAnnotationInput { | ||
private final CheckProjectComponent component; | ||
private final PsiFile psiFile; | ||
private final String fileContent; | ||
|
||
CheckAnnotationInput(CheckProjectComponent component, PsiFile psiFile, String fileContent) { | ||
this.component = component; | ||
this.psiFile = psiFile; | ||
this.fileContent = fileContent; | ||
} | ||
|
||
CheckProjectComponent getComponent() { | ||
return component; | ||
} | ||
|
||
String getCwd() { | ||
return psiFile.getProject().getBasePath(); | ||
} | ||
|
||
String getFilePath() { | ||
return psiFile.getVirtualFile().getPath(); | ||
} | ||
|
||
String getFileContent() { | ||
return fileContent; | ||
} | ||
|
||
} |
166 changes: 166 additions & 0 deletions
166
src/main/java/io/binx/cfnlint/plugin/CheckExternalAnnotator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package io.binx.cfnlint.plugin; | ||
|
||
import com.intellij.lang.annotation.Annotation; | ||
import com.intellij.lang.annotation.AnnotationHolder; | ||
import com.intellij.lang.annotation.ExternalAnnotator; | ||
import com.intellij.lang.annotation.HighlightSeverity; | ||
import com.intellij.notification.NotificationType; | ||
import com.intellij.openapi.diagnostic.Logger; | ||
import com.intellij.openapi.editor.Document; | ||
import com.intellij.openapi.editor.Editor; | ||
import com.intellij.openapi.fileEditor.FileDocumentManager; | ||
import com.intellij.openapi.util.TextRange; | ||
import com.intellij.openapi.vfs.VirtualFile; | ||
import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider; | ||
import com.intellij.psi.PsiDocumentManager; | ||
import com.intellij.psi.PsiFile; | ||
import com.intellij.util.DocumentUtil; | ||
import io.binx.cfnlint.plugin.utils.CheckResult; | ||
import io.binx.cfnlint.plugin.utils.CheckRunner; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public class CheckExternalAnnotator extends | ||
ExternalAnnotator<CheckAnnotationInput, AnnotationResult> { | ||
|
||
private static final Logger LOG = Logger.getInstance(CheckExternalAnnotator.class); | ||
|
||
@Nullable | ||
@Override | ||
public CheckAnnotationInput collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { | ||
return collectInformation(file); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public CheckAnnotationInput collectInformation(@NotNull PsiFile file) { | ||
if (file.getContext() != null) { | ||
return null; | ||
} | ||
VirtualFile virtualFile = file.getVirtualFile(); | ||
if (virtualFile == null || !virtualFile.isInLocalFileSystem()) { | ||
return null; | ||
} | ||
if (file.getViewProvider() instanceof MultiplePsiFilesPerDocumentFileViewProvider) { | ||
return null; | ||
} | ||
CheckProjectComponent component = file.getProject().getComponent(CheckProjectComponent.class); | ||
if (!component.isSettingsValid() || !component.isEnabled() || !isFile(file)) { | ||
return null; | ||
} | ||
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); | ||
boolean fileModified = fileDocumentManager.isFileModified(virtualFile); | ||
return new CheckAnnotationInput(component, file, fileModified ? file.getText() : null); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public AnnotationResult doAnnotate(CheckAnnotationInput input) { | ||
CheckProjectComponent component = input.getComponent(); | ||
try { | ||
CheckResult result = CheckRunner.runCheck(component.getSettings().executable, input.getCwd(), input.getFilePath(), input.getFileContent()); | ||
|
||
if (StringUtils.isNotEmpty(result.getErrorOutput())) { | ||
component.showInfoNotification(result.getErrorOutput(), NotificationType.WARNING); | ||
return null; | ||
} | ||
return new AnnotationResult(input, result); | ||
} catch (Exception e) { | ||
LOG.error("Error running inspection: ", e); | ||
component.showInfoNotification("Error running inspection: " + e.getMessage(), NotificationType.ERROR); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public void apply(@NotNull PsiFile file, AnnotationResult annotationResult, @NotNull AnnotationHolder holder) { | ||
if (annotationResult == null) { | ||
return; | ||
} | ||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); | ||
if (document == null) { | ||
return; | ||
} | ||
|
||
CheckProjectComponent component = annotationResult.getInput().getComponent(); | ||
for (CheckResult.Issue issue : annotationResult.getIssues()) { | ||
HighlightSeverity severity = getHighlightSeverity(issue, component.getSettings().treatAllIssuesAsWarnings); | ||
createAnnotation(holder, document, issue, severity, component); | ||
} | ||
} | ||
|
||
private static HighlightSeverity getHighlightSeverity(CheckResult.Issue issue, boolean treatAsWarnings) { | ||
switch (issue.level.toLowerCase()) { | ||
case "error": | ||
return treatAsWarnings ? HighlightSeverity.WARNING : HighlightSeverity.ERROR; | ||
case "warning": | ||
return HighlightSeverity.WARNING; | ||
case "info": | ||
return HighlightSeverity.INFORMATION; | ||
default: | ||
return HighlightSeverity.INFORMATION; | ||
} | ||
} | ||
|
||
@Nullable | ||
private Annotation createAnnotation(@NotNull AnnotationHolder holder, @NotNull Document document, @NotNull CheckResult.Issue issue, | ||
@NotNull HighlightSeverity severity, | ||
CheckProjectComponent component) { | ||
int errorLine = issue.location.start.lineNumber - 1; | ||
boolean showErrorOnWholeLine = component.getSettings().highlightWholeLine; | ||
|
||
if (errorLine < 0 || errorLine >= document.getLineCount()) { | ||
return null; | ||
} | ||
|
||
int lineStartOffset = document.getLineStartOffset(errorLine); | ||
int lineEndOffset = document.getLineEndOffset(errorLine); | ||
|
||
int errorLineStartOffset = appendNormalizeColumn(document, lineStartOffset, lineEndOffset, issue.location.start.columnNumber - 1); | ||
if (errorLineStartOffset == -1) { | ||
return null; | ||
} | ||
|
||
TextRange range; | ||
if (showErrorOnWholeLine) { | ||
int start = DocumentUtil.getFirstNonSpaceCharOffset(document, lineStartOffset, lineEndOffset); | ||
range = new TextRange(start, lineEndOffset); | ||
} else { | ||
range = new TextRange(errorLineStartOffset, errorLineStartOffset + 1); | ||
} | ||
|
||
Annotation annotation = holder.createAnnotation(severity, range, ": " + issue.getFormattedMessage()); | ||
if (annotation != null) { | ||
annotation.setAfterEndOfLine(errorLineStartOffset == lineEndOffset); | ||
} | ||
return annotation; | ||
} | ||
|
||
private int appendNormalizeColumn(@NotNull Document document, int startOffset, int endOffset, int column) { | ||
CharSequence text = document.getImmutableCharSequence(); | ||
int col = 0; | ||
for (int i = startOffset; i < endOffset; i++) { | ||
char c = text.charAt(i); | ||
col += (c == '\t' ? 8 : 1); | ||
if (col > column) { | ||
return i; | ||
} | ||
} | ||
return startOffset; | ||
} | ||
|
||
private static boolean isFile(PsiFile file) { | ||
// TODO move to settings? | ||
List<String> acceptedExtensions = Arrays.asList("yml", "yaml", "json"); | ||
boolean isCloudFormation = file.getFileType().getName().equals("CloudFormation"); | ||
String fileExtension = Optional.ofNullable(file.getVirtualFile()).map(VirtualFile::getExtension).orElse(""); | ||
return isCloudFormation || acceptedExtensions.contains(fileExtension); | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.