| 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()); |
| } |
| } |
| } |
| } |