blob: d1c83ec03952a58d75eb4a824040cb8893341118 [file] [log] [blame]
package com.intellij.json.codeinsight;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.json.JsonBundle;
import com.intellij.json.JsonElementTypes;
import com.intellij.json.psi.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* Compliance checks include
* <ul>
* <li>Usage of line and block commentaries</li>
* <li>Usage of single quoted strings</li>
* <li>Usage of identifiers (unqouted words)</li>
* <li>Not double quoted string literal is used as property key</li>
* </ul>
*
* @author Mikhail Golubev
*/
public class JsonStandardComplianceInspection extends LocalInspectionTool {
private static final Logger LOG = Logger.getInstance(JsonStandardComplianceInspection.class);
public boolean myWarnAboutComments = true;
@NotNull
public String getDisplayName() {
return JsonBundle.message("name.standard.compliance.inspection");
}
@NotNull
@Override
public HighlightDisplayLevel getDefaultLevel() {
return HighlightDisplayLevel.ERROR;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JsonElementVisitor() {
@Override
public void visitComment(PsiComment comment) {
if (myWarnAboutComments) {
if (JsonStandardComplianceProvider.shouldWarnAboutComment(comment)) {
holder.registerProblem(comment, JsonBundle.message("msg.compliance.problem.comments"), ProblemHighlightType.WEAK_WARNING);
}
}
}
@Override
public void visitStringLiteral(@NotNull JsonStringLiteral stringLiteral) {
if (stringLiteral.getText().startsWith("'")) {
holder
.registerProblem(stringLiteral, JsonBundle.message("msg.compliance.problem.single.quoted.strings"), new AddDoubleQuotesFix());
}
// May be illegal property key as well
super.visitStringLiteral(stringLiteral);
}
@Override
public void visitLiteral(@NotNull JsonLiteral literal) {
if (literal.getParent() instanceof JsonFile) {
holder.registerProblem(literal, JsonBundle.message("msg.compliance.problem.illegal.top.level.value"));
}
if (JsonPsiUtil.isPropertyKey(literal) && !(literal.getText().startsWith("\""))) {
holder.registerProblem(literal, JsonBundle.message("msg.compliance.problem.illegal.property.key"), new AddDoubleQuotesFix());
}
}
@Override
public void visitReferenceExpression(@NotNull JsonReferenceExpression reference) {
holder.registerProblem(reference, JsonBundle.message("msg.compliance.problem.identifier"), new AddDoubleQuotesFix());
// May be illegal property key as well
super.visitReferenceExpression(reference);
}
@Override
public void visitArray(@NotNull JsonArray array) {
final PsiElement trailingComma = findTrailingComma(array, JsonElementTypes.R_BRACKET);
if (trailingComma != null) {
holder.registerProblem(trailingComma, JsonBundle.message("msg.compliance.problem.trailing.comma"));
}
}
@Override
public void visitObject(@NotNull JsonObject object) {
final PsiElement trailingComma = findTrailingComma(object, JsonElementTypes.R_CURLY);
if (trailingComma != null) {
holder.registerProblem(trailingComma, JsonBundle.message("msg.compliance.problem.trailing.comma"));
}
}
};
}
@Nullable
private static PsiElement findTrailingComma(@NotNull JsonContainer container, @NotNull IElementType ending) {
final PsiElement lastChild = container.getLastChild();
if (lastChild.getNode().getElementType() != ending) {
return null;
}
final PsiElement beforeEnding = PsiTreeUtil.skipSiblingsBackward(lastChild, PsiComment.class, PsiWhiteSpace.class);
if (beforeEnding != null && beforeEnding.getNode().getElementType() == JsonElementTypes.COMMA) {
return beforeEnding;
}
return null;
}
@Override
public JComponent createOptionsPanel() {
final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this);
optionsPanel.addCheckbox(JsonBundle.message("option.warn.about.comments.name"), "myWarnAboutComments");
return optionsPanel;
}
private static class AddDoubleQuotesFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return JsonBundle.message("name.add.double.quotes.quickfix");
}
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element instanceof JsonLiteral || element instanceof JsonReferenceExpression) {
final String content = StringUtil.stripQuotesAroundValue(element.getText());
element.replace(new JsonElementGenerator(project).createStringLiteral(content));
}
else if (element != null) {
LOG.error("Quick fix was applied to unexpected element", element.getText(), element.getParent().getText());
}
}
}
}